<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/css/all.min.css" integrity="sha256-mUZM63G8m73Mcidfrv5E+Y61y7a12O5mW4ezU3bxqW4=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css@3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"tallgy.gitee.io","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":false,"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"}}</script><script src="/js/config.js"></script>
<meta name="description" content="只是一个知识的搬运工">
<meta property="og:type" content="website">
<meta property="og:title" content="tallgy&#39;s blog">
<meta property="og:url" content="http://tallgy.gitee.io/page/3/index.html">
<meta property="og:site_name" content="tallgy&#39;s blog">
<meta property="og:description" content="只是一个知识的搬运工">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="tallgy">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="http://tallgy.gitee.io/page/3/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"zh-CN","comments":"","permalink":"","path":"page/3/index.html","title":""}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>tallgy's blog</title>
  




  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">tallgy's blog</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>







</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">tallgy</p>
  <div class="site-description" itemprop="description">只是一个知识的搬运工</div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">107</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">32</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">74</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://tallgy.gitee.io/2022/03/05/JavaScript/ES6/JavaScript-ES6/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tallgy">
      <meta itemprop="description" content="只是一个知识的搬运工">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="tallgy's blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/03/05/JavaScript/ES6/JavaScript-ES6/" class="post-title-link" itemprop="url">JavaScript-ES6</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-03-05 21:53:11" itemprop="dateCreated datePublished" datetime="2022-03-05T21:53:11+08:00">2022-03-05</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2023-06-25 16:57:29" itemprop="dateModified" datetime="2023-06-25T16:57:29+08:00">2023-06-25</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JavaScript/" itemprop="url" rel="index"><span itemprop="name">JavaScript</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JavaScript/%E9%9A%8F%E7%AC%94/" itemprop="url" rel="index"><span itemprop="name">随笔</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="ES6"><a href="#ES6" class="headerlink" title="ES6"></a>ES6</h1><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>​        ECMAScript，简单来说就是发布规定脚本语言的标准。</p>
<p>​        ES6，全称就是 ECMAScript 6.0，是在 2015 年正式通过的。</p>
<h2 id="内容"><a href="#内容" class="headerlink" title="内容"></a>内容</h2><h3 id="let-const"><a href="#let-const" class="headerlink" title="let const"></a>let const</h3><p>​        新建了 let 和 const 声明</p>
<p>​        let const 和 var 的区别：</p>
<ul>
<li>作用域： let const 的 作用域 处于括号级作用域，var 是一个函数级的作用域，关于作用域的问题，我们后面在进行详细的学习</li>
<li>变量提升：let ，const 不会出现变量提升。而var会出现变量提升的效果。</li>
<li>const 常量，不可变。</li>
</ul>
<h3 id="模板字符串"><a href="#模板字符串" class="headerlink" title="模板字符串"></a>模板字符串</h3><p>​        ` `，ES6增加了模板字符串，可以进行换行，以及可以在字符串中拼接表达式 ${}。</p>
<h3 id="箭头函数"><a href="#箭头函数" class="headerlink" title="箭头函数"></a>箭头函数</h3><p>​        添加了 箭头函数，() =&gt; {}</p>
<ul>
<li>函数上下文：this 的指向不同，普通函数的this指向处于调用时的调用栈的this指向。而箭头函数的函数上下文在定义时已经确定了，无法改变。</li>
<li>不存在构造器函数：因为不存在构造器函数，所以不能使用new方法，会报错。</li>
</ul>
<h3 id="函数参数默认值"><a href="#函数参数默认值" class="headerlink" title="函数参数默认值"></a>函数参数默认值</h3><p>​        </p>
<h3 id="Spread-Rest-操作符"><a href="#Spread-Rest-操作符" class="headerlink" title="Spread/Rest 操作符"></a>Spread/Rest 操作符</h3><p>​        简单来说就是 … ，我们可以使用 … 将数组解开为单个元素，也能将元素合并为一个数组。</p>
<h3 id="二进制和八进制的字面量"><a href="#二进制和八进制的字面量" class="headerlink" title="二进制和八进制的字面量"></a>二进制和八进制的字面量</h3><ul>
<li>0o xx：八进制</li>
<li>0b xx：二进制</li>
</ul>
<h3 id="解构赋值"><a href="#解构赋值" class="headerlink" title="解构赋值"></a>解构赋值</h3><p>​        可以将对象和数组进行解构</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">let &#123;a&#125; = obj</span><br><span class="line">let [c, d] = arr</span><br></pre></td></tr></table></figure>

<p>​        注意：</p>
<ul>
<li>对象的解构，是针对名字进行的，没有的键会为undefined，我们也可以赋予默认值</li>
<li>数组的解构是通过下标的对应，对于不要的我们可以使用 ,, 将其赋值给不存在的变量，也可以使用 spread 和 rest 操作符进行合并赋值。</li>
</ul>
<h3 id="类"><a href="#类" class="headerlink" title="类"></a>类</h3><p>​        ES6 新增了一个关于 Class 的语法糖。</p>
<p>​        类的ES5的实现</p>
<p>​        new 方法的过程：</p>
<ul>
<li>创建一个this的空对象，同时继承函数的原型。</li>
<li>然后再执行方法。</li>
<li>如果返回的不是对象，那么就返回this。否则返回对象。</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">一次new操作：</span><br><span class="line">创建一个this，原型指向了fn的原型</span><br><span class="line">let this = Object.create(fn.prototype)</span><br><span class="line"></span><br><span class="line">调用fn</span><br><span class="line">let res = fn.apply(this, args)</span><br><span class="line"></span><br><span class="line">通过判断res的类型来判断返回是res还是this</span><br><span class="line">return typeof res === &#x27;object&#x27; ? res : this;</span><br></pre></td></tr></table></figure>



<p>构造器</p>
<p>​        ES6的构造器，就是类在进行new方法时会执行的一个方法。对于没有构造器的方法不能使用new方法。</p>
<p>​        类的构造器，实际上就是在new方法执行时会执行的函数</p>
<p>​        同时使用 class 创建的函数，是一个 不可枚举的。存在于实例的 proto 中。 fn.proto === Fn.prototype 。</p>
<p>super</p>
<p>​        构造器内部可以调用super方法执行父类的构造器。</p>
<ul>
<li><p>在普通方法中，super指向了父类的原型，this是子类的实例</p>
</li>
<li><p>在静态方法中，super指向了父类，this就是子类</p>
</li>
<li><p>同时，不能直接输出super，super需要以方法或属性的形式调用 super() super.fn() super.cc</p>
</li>
</ul>
<p>继承</p>
<p>​        class A extend B</p>
<ul>
<li>继承可以使用super来调用父类的方法和构造器</li>
<li>所谓的继承就是，可以将父类的方法放到子类进行调用，同时父类的构造器生成的属性也会放到子类。</li>
<li>所以我们又知道，子类的实例的属性是子类的构造器的执行的结果。子类实例的原型就是子类的原型。子类原型的proto就是继承的类的原型。</li>
</ul>
<p>继承的ES5的实现</p>
<p>我们需要注意的点：</p>
<ul>
<li>需要执行父类的构造器方法</li>
<li>需要将父类的原型设置为自身的原型，一般来说，原型是不会赋予属性的，所以一般默认都是一个空的对象，然后这个对象的proto属性是Object.prototype。</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">function Child() &#123;</span><br><span class="line">	// 调用 父函数的方法，使用call方法进行调用，</span><br><span class="line">	// 这里思考的话需要考虑，如果父函数存在对象返回值的情况，那么会返回的是对象，而不是call方法了。这个需要进行解决。</span><br><span class="line">	Parent.call(this, args)</span><br><span class="line">	</span><br><span class="line">	// 这里写自己的方法</span><br><span class="line">	...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">实现原型的继承</span><br><span class="line">(function() &#123;</span><br><span class="line">	function Fn() &#123;&#125;</span><br><span class="line">	Fn.prototype = Parent.prototype</span><br><span class="line">	Child.prototype = new Fn()</span><br><span class="line">	Child.prototype.constructor = Child</span><br><span class="line">&#125;)()</span><br><span class="line"></span><br><span class="line">因为一个new方法就是要，创建一个原型指向了父类的原型的一个对象，然后执行构造器函数</span><br><span class="line">区别就是使用了new方法，然后中间会创建一个对象，对象的原型指向了父类原型，然后我们使用constructor修改了对象的构造器指向，就不会修改到父类的构造器了。</span><br><span class="line"></span><br><span class="line">Child.prototype = Parent.prototype</span><br><span class="line">// 这里是为了修改 构造器的指向，但是因为我们是直接复制的原型，所以这里的修改会影响到父类的构造器的指向。</span><br><span class="line">Child.prototype.constructor = Child</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">class Parent &#123;</span><br><span class="line">  constructor() &#123;</span><br><span class="line">    this.p = 1</span><br><span class="line"></span><br><span class="line">    return &#123;</span><br><span class="line">      p: 2,</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">class Child extends Parent &#123;</span><br><span class="line">  constructor() &#123;</span><br><span class="line">    super()</span><br><span class="line">    this.c = 3</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">我们执行之后，发现，返回值 p=2，而不是1.</span><br><span class="line">这里我刚刚百度了一下，在继承里面，ES6，是先super，然后再this，所以对于继承里面，如果没用super，将不能使用this。所以我们大概知道就是 super，先像new一样，然后通过返回将其加入this，然后再使用。</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">简单的一个实现 Super</span><br><span class="line">使用：Super.call(this, Parent)</span><br><span class="line">思路：就是先创建一个空对象，进行调用，然后通过返回值类型来判断，哪个应该合并为this</span><br><span class="line">	缺点：因为是使用对象，而并不是真正的上下文this，所以不能使用一些需要依靠this操作的行为。以及赋值给this是依靠的 Object.assign 进行的操作。</span><br><span class="line"></span><br><span class="line">function Super(Parent) &#123;</span><br><span class="line">  const ctx = &#123;&#125;</span><br><span class="line">  const res = Parent.call(ctx)</span><br><span class="line">  const r = typeof res === &#x27;object&#x27; ? res : ctx</span><br><span class="line"></span><br><span class="line">  Object.assign(this, r)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="Promise"><a href="#Promise" class="headerlink" title="Promise"></a>Promise</h3><p>​        简单来说就是用来传递异步的消息。</p>
<p>​        在以往，我们使用异步的形式都是使用的回调函数，就是将回调函数作为参数传递过去，然后方法那边的在异步结束之后再调用这个函数。</p>
<p>​        同时Promise存在三个状态：</p>
<ul>
<li>pending：初始状态</li>
<li>fulfilled：成功的状态</li>
<li>rejected：失败的状态</li>
</ul>
<p>​    <strong>注意</strong>：当状态从pending进入另外两个状态是，将不能改变了。</p>
<p>​        只有异步操作的结果，可以决定当前是哪一种状态，任何其他操作都无法改变这个状态。</p>
<p><img src="/.io//promises.png" alt="promises"></p>
<h4 id="Promise-then"><a href="#Promise-then" class="headerlink" title="Promise.then():"></a>Promise.then():</h4><p>​        then() 方法返回一个 Promise (en-US)。它最多需要有两个参数：Promise 的成功和失败情况的回调函数。</p>
<p>​        两个参数：代表了 res 和 err（可选）</p>
<p>​        如果是Promise.then()，如果后面再有 .then() 但是却没有 return一个Promise的，那么就会进入 resolve(undefined) 的情况</p>
<p>返回值：</p>
<p>分为：返回一个值，没有返回，接受状态，拒绝状态，和未定状态。</p>
<ul>
<li>返回了一个值，那么 <code>then</code> 返回的 Promise 将会成为接受状态，并且将返回的值作为接受状态的回调函数的参数值。</li>
<li>没有返回任何值，那么 <code>then</code> 返回的 Promise 将会成为接受状态，并且该接受状态的回调函数的参数值为 <code>undefined</code>。</li>
<li>抛出一个错误，那么 <code>then</code> 返回的 Promise 将会成为拒绝状态，并且将抛出的错误作为拒绝状态的回调函数的参数值。</li>
<li>返回一个已经是接受状态的 Promise，那么 <code>then</code> 返回的 Promise 也会成为接受状态，并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。</li>
<li>返回一个已经是拒绝状态的 Promise，那么 <code>then</code> 返回的 Promise 也会成为拒绝状态，并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。</li>
<li>返回一个未定状态（<code>pending</code>）的 Promise，那么 <code>then</code> 返回 Promise 的状态也是未定的，并且它的终态与那个 Promise 的终态相同；同时，它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。</li>
</ul>
<h4 id="Promise-catch"><a href="#Promise-catch" class="headerlink" title="Promise.catch():"></a>Promise.catch():</h4><p>​        catch() 方法返回一个Promise (en-US)，并且处理拒绝的情况。它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。 (事实上, calling obj.catch(onRejected) 内部calls obj.then(undefined, onRejected)).</p>
<p>​        参数：onRejected</p>
<p>返回值：<br>        一个Promise.</p>
<p>​        默认一个 resolve，可以返回rejected</p>
<h4 id="Promise-finally"><a href="#Promise-finally" class="headerlink" title="Promise.finally():"></a>Promise.finally():</h4><p>​        finally() 方法返回一个Promise。在promise结束时，无论结果是fulfilled或者是rejected，都会执行指定的回调函数。这为在Promise是否成功完成后都需要执行的代码提供了一种方式。这避免了同样的语句需要在then()和catch()中各写一次的情况。</p>
<p>​        参数：onFinally</p>
<h4 id="Promise-all"><a href="#Promise-all" class="headerlink" title="Promise.all:"></a>Promise.all:</h4><p>​        Promise.all() 方法接收一个promise的iterable类型（注：Array，Map，Set都属于ES6的iterable类型）的输入，并且只返回一个Promise实例， 那个输入的所有promise的resolve回调的结果是一个数组。</p>
<p>​        接受一个 promise的iterable类型</p>
<p>​        就是可迭代的promise。需要全部返回resolve才能resolve</p>
<p>​        只要任何一个输入的promise的reject回调执行或者输入不合法的promise就会立即抛出错误，并且reject的是第一个抛出的错误信息。</p>
<h4 id="Promise-allSettled"><a href="#Promise-allSettled" class="headerlink" title="Promise.allSettled:"></a>Promise.allSettled:</h4><h4 id="Promise-any"><a href="#Promise-any" class="headerlink" title="Promise.any:"></a>Promise.any:</h4><p>​        Promise.any()<code>接收一个 Promise 可迭代对象，只要其中的一个</code>promise<code>成功，就返回那个已经成功的</code>promise</p>
<h4 id="Promise-race"><a href="#Promise-race" class="headerlink" title="Promise.race:"></a>Promise.race:</h4><p>​        将与第一个传递的promise相同的完成方式被完成。就是说，在一个可迭代对象里面，我们将获取到所返回的第一个结果，不管是resolve和rejected。</p>
<h3 id="generator-生成器"><a href="#generator-生成器" class="headerlink" title="generator 生成器"></a>generator 生成器</h3><p>​        生成器对象是由一个 generator function 返回的,并且它符合可迭代协议和迭代器协议。</p>
<p>​        实例：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">一个无限的迭代器</span><br><span class="line"></span><br><span class="line">function* idMaker()&#123;</span><br><span class="line">    let index = 0;</span><br><span class="line">    while(true)</span><br><span class="line">        yield index++;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">let gen = idMaker(); // &quot;Generator &#123; &#125;&quot;</span><br><span class="line"></span><br><span class="line">console.log(gen.next().value);</span><br><span class="line">// 0</span><br><span class="line">console.log(gen.next().value);</span><br><span class="line">// 1</span><br><span class="line">console.log(gen.next().value);</span><br><span class="line">// 2</span><br><span class="line">// ...</span><br></pre></td></tr></table></figure>

<p>​        通过这个我们可以发现一个可迭代协议和可迭代对象，可迭代协议是一个Es6的补充规范。</p>
<p>​        简单来说，一个可迭代对象就需要实现这个可迭代协议，会实现一个 @@iterator 方法。（原型链上存在也可以），在for of 的循环和使用 spread和rest操作符的时候就会使用上这个可迭代协议。</p>
<p>​        可迭代协议的方法就是返回一个next方法。方法返回一个 done 和 value 属性。</p>
<p>​        而这个就是我们生成器的返回的特点，简单来说就是我们生成器就是一个满足可迭代协议而存在的一个方法。</p>
<h3 id="模块化"><a href="#模块化" class="headerlink" title="模块化"></a>模块化</h3><p>​        模块化简单分为 CommonJS、AMD、CMD和现在的ES6</p>
<p>​        其中 AMD 和 CMD 都是参照的 CommonJS</p>
<p>CommonJS：</p>
<p>​        NodeJs发布的。</p>
<p>​        require引入、exports导出</p>
<p>AMD、CMD:</p>
<p>​        define 定义模块，require实现加载。</p>
<p>ES6：</p>
<p>​        使用 import 导入</p>
<p>​        export 导出</p>
<p>​        关键字有import，export，default，as，from。</p>
<p>ES6 和 CommonJS的区别：</p>
<ul>
<li>ES6 是浅拷贝，修改会影响原理的值。<ul>
<li>CommonJS的深拷贝</li>
</ul>
</li>
<li>CommonJS是运行时加载。<ul>
<li>ES6是编译时输出接口，可以单独加载接口。</li>
</ul>
</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://tallgy.gitee.io/2022/03/03/JavaScript/%E5%AF%B9%E8%B1%A1/Object-proto/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tallgy">
      <meta itemprop="description" content="只是一个知识的搬运工">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="tallgy's blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/03/03/JavaScript/%E5%AF%B9%E8%B1%A1/Object-proto/" class="post-title-link" itemprop="url">Object.__proto__</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-03-03 11:07:45" itemprop="dateCreated datePublished" datetime="2022-03-03T11:07:45+08:00">2022-03-03</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2023-06-25 16:57:29" itemprop="dateModified" datetime="2023-06-25T16:57:29+08:00">2023-06-25</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JavaScript/" itemprop="url" rel="index"><span itemprop="name">JavaScript</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JavaScript/%E9%9A%8F%E7%AC%94/" itemprop="url" rel="index"><span itemprop="name">随笔</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="关于-属性原型的一个问题"><a href="#关于-属性原型的一个问题" class="headerlink" title="关于 属性原型的一个问题"></a>关于 属性原型的一个问题</h1><p>​        先说问题和总结</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">const obj = &#123;</span><br><span class="line">  a: 1,</span><br><span class="line">&#125;</span><br><span class="line">obj.__proto__.x = 1</span><br><span class="line"></span><br><span class="line">// 可以这样直接访问</span><br><span class="line">x.log</span><br></pre></td></tr></table></figure>

<p>​        总结：</p>
<p>​        因为全局的上下文的 proto 也是 Object.prototype。所以我们修改了 obj.proto 实际上也修改了 Object.prototype。所以，不要随意修改高层的原型，容易出大问题啊。</p>
<p>​        首先我们可以知道，一个对象，存在一个 prototype 原型，然后在创建一个实例的时候，会将其创建为一个 proto 的内部属性进行赋值。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">const obj = &#123;</span><br><span class="line">  a: 1,</span><br><span class="line">&#125;</span><br><span class="line">obj.__proto__.x = 1</span><br></pre></td></tr></table></figure>

<p>​        如果上面这样写，就是说，设置了 obj 的 proto 属性，然后就会在 Object.prototype 里面创建上这个属性，因为 proto 就是引用的 prototype，对于一个对象，他的引用就是 Object 的 prototype 了。</p>
<p>​        这个其实没啥，但是我们却可以通过一个例子发现一个问题。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">const obj = &#123;</span><br><span class="line">  a: 1,</span><br><span class="line">&#125;</span><br><span class="line">obj.__proto__.x = 1</span><br><span class="line">console.log(x)</span><br></pre></td></tr></table></figure>

<p>​        看看这里，我先创建一个对象 obj，然后对其原型创建一个x的属性，然后我们如果直接访问x，则会发现，还真的可以访问。。</p>
<p>​        并不会报错。这里的原因又是为什么呢？</p>
<ul>
<li><p>简单来说就是，运行环境下，也是存在一个上下文的，存在上下文，那么便会有上层原型，JS里面所有的原型的最终的指向就是 Object.prototype 都会挨着挨着这样指上去。所以，一切的最终指向都是 顶层的Object的 prototype 。然后，就是运行环境的上下文问题了。</p>
<ul>
<li>```<br>const obj = {<br>  a: 1,<br>}<br>obj.<strong>proto</strong>.x = 1<br>console.log(x)<br>function c() {}<br>console.log(c.x)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">* 运行环境的上下文，我们可以通过输出this，来知道上下文是什么，当然，输出一般都是一个空对象或者是window，但是这个并不是重点，重点是，为什么x，可以输出，因为他仅仅只是放在了obj的proto里面</span><br><span class="line"></span><br><span class="line">* 我们可以知道，JS 的原型最终都是指向的 Object.prototype 所以，obj.proto 其实也是指向的 这个，因为他这个是顶层的一个 new Object，然后我们修改了 proto 就是修改了 prototype，所以 JS 环境下的 prototype 就被修改了，然后我们访问 x，然后，在本地上下文不存在，于是就会去原型进行查找，this.proto 就是 prototype，所以这样的指向最后就是 prototype了。</span><br><span class="line"></span><br><span class="line">* ```</span><br><span class="line">  console.log(x, this.__proto__, Object.prototype)</span><br><span class="line">  this.__proto__ === Object.prototype ： true</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://tallgy.gitee.io/2022/03/01/JavaScript/%E5%AF%B9%E8%B1%A1/Proxy%E5%92%8Cdefineproperty/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tallgy">
      <meta itemprop="description" content="只是一个知识的搬运工">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="tallgy's blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/03/01/JavaScript/%E5%AF%B9%E8%B1%A1/Proxy%E5%92%8Cdefineproperty/" class="post-title-link" itemprop="url">Proxy和defineproperty</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-03-01 22:55:17" itemprop="dateCreated datePublished" datetime="2022-03-01T22:55:17+08:00">2022-03-01</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2023-06-25 16:57:29" itemprop="dateModified" datetime="2023-06-25T16:57:29+08:00">2023-06-25</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JavaScript/" itemprop="url" rel="index"><span itemprop="name">JavaScript</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JavaScript/%E9%9A%8F%E7%AC%94/" itemprop="url" rel="index"><span itemprop="name">随笔</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="Proxy-和-defineproperty-的区别和使用"><a href="#Proxy-和-defineproperty-的区别和使用" class="headerlink" title="Proxy 和 defineproperty 的区别和使用"></a>Proxy 和 defineproperty 的区别和使用</h1><p>​        首先我们学习了 vue 的响应式之后就会知道，vue2 的响应式是根据 Object.defineproperty 进行的处理，通过编写里面的 getter 和 setter 方法。在getter 和 setter 方法里面进行了处理。getter 方法里面将对应的DOM加入了 dep数组，然后</p>
<ul>
<li>data数据通过使用 observer 转换成了 getter/setter 形式的来追踪变化。</li>
<li>当通过 watcher 读取到数据时，会触发到 getter 将其添加到 依赖中。</li>
<li>当数据发生变化时，会触发setter，然后想 dep 中的依赖（watcher） 发送通知。</li>
<li>watcher 接收到通知之后，则会发送通知。然后便会触发视图的更新。</li>
</ul>
<p>​        但是同时 Object.defineproperty 也有对应的缺点，比如这个是针对对象进行的监听，只能监听到对象的改变，而不能监听到数组的变化。</p>
<p>​        所以就有了 vue3 使用的 proxy，Proxy 对象用于创建一个对象的代理，从而实现基本操作的拦截和自定义（如属性查找、赋值、枚举、函数调用等）。proxy的特点是，可以深度监听，并且可以监听 新增和删除的属性。还可以监听数组的变化。</p>
<h2 id="Object-defineproperty"><a href="#Object-defineproperty" class="headerlink" title="Object.defineproperty"></a>Object.defineproperty</h2><ul>
<li><p>这个方法会直接 在一个对象上定义一个新属性，或者修改一个一个对象的现有属性，并返回此对象。</p>
</li>
<li><p>```<br>const object1 = {};</p>
<p>Object.defineProperty(object1, ‘property1’, {<br>  value: 42,<br>  writable: false<br>});</p>
<p>object1.property1 = 77;<br>// throws an error in strict mode</p>
<p>console.log(object1.property1);<br>// expected output: 42</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">* Object.defineProperty(obj, prop, descriptor)</span><br><span class="line"></span><br><span class="line">* 参数</span><br><span class="line"></span><br><span class="line">  * obj： 要定义的对象</span><br><span class="line">  * prop： 要定义或修改的属性的名称或者 Symbol</span><br><span class="line">  * descriptor： 要定义或修改的属性描述符。</span><br><span class="line"></span><br><span class="line">* 返回值： 被传递给函数的对象。</span><br><span class="line"></span><br><span class="line">&gt; 备注：在ES6中，由于 Symbol类型的特殊性，用Symbol类型的值来做对象的key与常规的定义或修改不同，而Object.defineProperty 是定义key为Symbol的属性的方法之一。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">简单示例：</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>const obj = {<br>a: 1,<br>}</p>
</li>
</ul>
<p>Object.defineProperty(obj, ‘b’, {<br>  value: 2,<br>})</p>
<p>Object.defineProperty(obj, ‘a’, {<br>  get() {<br>    return 1;<br>  }<br>})</p>
<p>注意点：<br>数据描述符和存取描述符不能混合使用</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">### 数据描述符 和 存储描述符 </span><br><span class="line"></span><br><span class="line">| `configurable` | `enumerable` | `value` | `writable` | `get`  | `set`  |        |</span><br><span class="line">| -------------- | ------------ | ------- | ---------- | ------ | ------ | ------ |</span><br><span class="line">| 数据描述符     | 可以         | 可以    | 可以       | 可以   | 不可以 | 不可以 |</span><br><span class="line">| 存取描述符     | 可以         | 可以    | 不可以     | 不可以 | 可以   | 可以   |</span><br><span class="line"></span><br><span class="line">​		简单来说，就是数据描述符和存储描述符是不能混用的。混用会报错。</span><br><span class="line"></span><br><span class="line">* 数据描述符：value， writable</span><br><span class="line">* 存取描述符： get， set</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">​		那么我们可以知道，因为数据描述符和存取描述符不能同时进行创建和使用。我们可以查看Vue2的源码，Vue2里面是通过 defineReactive 进行创建的响应式，一个 defineReactive 代表的就是一个value，内部会维护一个 Dep，通过 Dep.depend() 和 Dep.notify 来进行依赖添加和 通知。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>observe(方法) —()—&gt;&gt; Observer(类) —( walk )—&gt;&gt; defineReactive(方法) —(line:178)—&gt;&gt; observe</p>
<p>就是这样的一个递归形式，从 observe 里面通过判断 不是对象 或者 属于VNode来结束递归</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure>
<p>// 注意: defineProperty 的get 方法是可以重复进行定义的。</p>
<p>const property = Object.getOwnPropertyDescriptor(obj, key);</p>
<p>const getter = property.get</p>
<p>Object.defineProperty(obj, key, {<br>    get() {<br>        const value = getter.call(obj)</p>
<pre><code>    // 处理响应式
    ...
    
    return value
&#125;
</code></pre>
<p>})</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure>
<p>// 同时记住，不能这样写，会出现，无限递归的情况，vue2 的写法是，将getter方法取出来，直接运行getter方法。<br>Object.defineProperty(o, ‘a’, {<br>  get() {<br>    console.log(‘q’)<br>    return o[‘a’]<br>  }<br>})</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">## Proxy</span><br><span class="line"></span><br><span class="line">​		**Proxy** 对象用于创建一个对象的代理，从而实现基本操作的拦截和自定义（如属性查找、赋值、枚举、函数调用等）。</span><br><span class="line"></span><br><span class="line">语法</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>const p = new Proxy(target, handler)</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">### Proxy.revocable</span><br><span class="line"></span><br><span class="line">​		与此对应的还有一个 Proxy.revocable 方法，它的使用和 new Proxy 基本一样，但是它的返回是一个包含了 代理对象本身和它的撤销方法的可撤销 Proxy 对象。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>Proxy.revocable(target, handler)</p>
<p>返回一个对象，包含了 proxy(代理对象本身) 和 revoke(撤销方法)</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		注意：</span><br><span class="line"></span><br><span class="line">​		一旦某个代理对象被撤销，它将变得几乎完全不可调用，在它身上执行任何的**可代理操作**都会抛出 [`TypeError`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypeError) 异常（注意，可代理操作一共有 [`14 种`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy#methods_of_the_handler_object)，执行这 14 种操作以外的操作不会抛出异常）。一旦被撤销，这个代理对象便不可能被直接恢复到原来的状态，同时和它关联的**目标对象**以及**处理器对象**都有可能被垃圾回收掉。再次调用撤销方法 `revoke()` 则不会有任何效果，但也不会报错。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>// 定义的 handler，这里定义了一个get方法，如果 prop 不存在于<br>const handler = {<br>    get: function(obj, prop) {<br>        return prop in obj ? obj[prop] : 37;<br>    }<br>};</p>
<p>const p = new Proxy({}, handler);<br>p.a = 1;<br>p.b = undefined;</p>
<p>console.log(p.a, p.b);      // 1, undefined<br>console.log(‘c’ in p, p.c); // false, 37</p>
<p>使用 revocable 进行创建一个可撤销的Proxy代理。<br>const revocable = Proxy.revocable({}, handler)<br>const proxy = revocable.proxy<br>revocable.revoke();</p>
<p>console.log(proxy.foo); // 抛出 TypeError<br>proxy.foo = 1           // 还是 TypeError<br>delete proxy.foo;       // 又是 TypeError<br>typeof proxy            // “object”，因为 typeof 不属于可代理操作</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		和 Object.defineproperty 一样创建多个get方法的情况</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>const obj = {}</p>
<p>let p = new Proxy(obj, {<br>  get(target, prop) {<br>    console.log(target)<br>    console.log(prop)<br>    return target[prop]<br>  }<br>})</p>
<p>p = new Proxy(p, {<br>  get(target, prop) {<br>    console.log(‘target’, target)<br>    console.log(‘prop’, prop)<br>    return target[prop]<br>  }<br>})</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		通过 Proxy 和 Object.defineproperty 的使用，我们可以知道几个不同的地方</span><br><span class="line"></span><br><span class="line">* Proxy的get方法带有参数，第一个是target，第二个prop。有的携带第三个参数newValue，defineproperty：不携带参数。</span><br><span class="line">* Proxy的get方法里面，target[prop]，不会再次触发get方法。defineproprety里面如果再次使用 target[prop] 会多次触发get方法。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### Proxy 的方法</span><br><span class="line"></span><br><span class="line">#### handler.apply() </span><br><span class="line"></span><br><span class="line">**`handler.apply()`** 方法用于拦截函数的调用。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>var p = new Proxy(target, {<br>  apply: function(target, thisArg, argumentsList) {<br>  }<br>});</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">参数：</span><br><span class="line"></span><br><span class="line">* target: 目标对象</span><br><span class="line">* thisArg： 被调用时的上下文对象</span><br><span class="line">* argumentsList： 被调用时的参数数组</span><br><span class="line"></span><br><span class="line">返回值：</span><br><span class="line"></span><br><span class="line">* 可以返回任何值</span><br><span class="line"></span><br><span class="line">返回值就是显示的内容</span><br><span class="line"></span><br><span class="line">拦截：</span><br><span class="line"></span><br><span class="line">- `proxy(...args)`</span><br><span class="line">- [`Function.prototype.apply()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) 和 [`Function.prototype.call()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call)</span><br><span class="line">- [`Reflect.apply()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/apply)</span><br><span class="line"></span><br><span class="line">约束：</span><br><span class="line"></span><br><span class="line">​		如果违反了以下约束，代理将抛出一个TypeError：</span><br><span class="line"></span><br><span class="line">​		`target`必须是可被调用的。也就是说，它必须是一个函数对象。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### handler.construct()</span><br><span class="line"></span><br><span class="line">​		`**handler.construct()**` 方法用于拦截[`new`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/new) 操作符. 为了使new操作符在生成的Proxy对象上生效，用于初始化代理的目标对象自身必须具有[[Construct]]内部方法（即 `new target` 必须是有效的）。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### handler.defineProperty()</span><br><span class="line"></span><br><span class="line">​		**`handler.defineProperty()`** 用于拦截对对象的 [`Object.defineProperty()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) 操作。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### handler.deleteProperty()</span><br><span class="line"></span><br><span class="line">​		**`handler.deleteProperty()`** 方法用于拦截对对象属性的 [`delete`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/delete) 操作。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### handler.get()</span><br><span class="line"></span><br><span class="line">​		**`handler.get()`** 方法用于拦截对象的读取属性操作。</span><br><span class="line"></span><br><span class="line">​		我们目前就先了解这个，因为这个就是vue3的架构里面使用的响应式的原理。</span><br><span class="line"></span><br><span class="line">语法:</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>var p = new Proxy(target, {<br>  get: function(target, property, receiver) {<br>  }<br>});</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">参数:</span><br><span class="line"></span><br><span class="line">* target：目标对象</span><br><span class="line">* property：被获取的属性名</span><br><span class="line">* receiver：Proxy或者继承Proxy的对象</span><br><span class="line"></span><br><span class="line">返回值:</span><br><span class="line"></span><br><span class="line">​		可以返回任何值。</span><br><span class="line"></span><br><span class="line">拦截：</span><br><span class="line"></span><br><span class="line">​		该方法会拦截目标对象的以下操作:</span><br><span class="line"></span><br><span class="line">- 访问属性: `proxy[foo]和` `proxy.bar`</span><br><span class="line"></span><br><span class="line">- 访问原型链上的属性: `Object.create(proxy)[foo]`</span><br><span class="line"></span><br><span class="line">- [`Reflect.get()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/get)</span><br><span class="line"></span><br><span class="line">  - `**Reflect**`**`.get()`**方法与从 对象 (`target[propertyKey]`) 中读取属性类似，但它是通过一个函数执行来操作的。</span><br><span class="line"></span><br><span class="line">  - ```</span><br><span class="line">    // Object</span><br><span class="line">    var obj = &#123; x: 1, y: 2 &#125;;</span><br><span class="line">    Reflect.get(obj, &quot;x&quot;); // 1</span><br><span class="line">    </span><br><span class="line">    // Array</span><br><span class="line">    Reflect.get([&quot;zero&quot;, &quot;one&quot;], 1); // &quot;one&quot;</span><br><span class="line">    </span><br><span class="line">    // Proxy with a get handler</span><br><span class="line">    var x = &#123;p: 1&#125;;</span><br><span class="line">    var obj = new Proxy(x, &#123;</span><br><span class="line">      get(t, k, r) &#123; return k + &quot;bar&quot;; &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    Reflect.get(obj, &quot;foo&quot;); // &quot;foobar&quot;</span><br></pre></td></tr></table></figure>

<p>约束：</p>
<p>​        如果违背了以下的约束，proxy会抛出 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypeError"><code>TypeError</code></a>:</p>
<ul>
<li><p>如果要访问的目标属性是不可写以及不可配置的，则返回的值必须与该目标属性的值相同。</p>
<ul>
<li><p>意思就是说，对于 writeable 和 configurable 为 false，则返回值必须要相等，否则报错。</p>
</li>
<li><p>```<br>Object.defineProperty(obj, ‘b’, {<br>  value: 2,<br>  writable: false<br>})</p>
<p>let p = new Proxy(obj, {<br>  get(target, prop) {</p>
<pre><code>return 21;
</code></pre>
<p>  }<br>})</p>
<p>p.b 会报错， 因为返回值不相等，但是如果返回值改为相等就不会报错。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">- 如果要访问的目标属性没有配置访问方法，即get方法是undefined的，则返回值必须为undefined。</span><br><span class="line"></span><br><span class="line">  - 大概意思是不可访问的情况下，访问的get返回需要为 undefined。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### handler.getOwnPropertyDescriptor()</span><br><span class="line"></span><br><span class="line">​		**`handler.getOwnPropertyDescriptor()`** 方法是 [`Object.getOwnPropertyDescriptor()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor) 的钩子。</span><br><span class="line"></span><br><span class="line">​		**`Object.getOwnPropertyDescriptor()`** 方法返回指定对象上一个自有属性对应的属性描述符。（自有属性指的是直接赋予该对象的属性，不需要从原型链上进行查找的属性）</span><br><span class="line"></span><br><span class="line">语法：</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>var p = new Proxy(target, {<br>getOwnPropertyDescriptor: function(target, prop) {<br>}<br>});</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">拦截：</span><br><span class="line"></span><br><span class="line">​		这个陷阱可以拦截这些操作：</span><br><span class="line"></span><br><span class="line">- [`Object.getOwnPropertyDescriptor()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor)</span><br><span class="line">- [`Reflect.getOwnPropertyDescriptor()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getOwnPropertyDescriptor)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### handler.getPrototypeOf()</span><br><span class="line"></span><br><span class="line">​		**`handler.getPrototypeOf()`** 是一个代理（Proxy）方法，当读取代理对象的原型时，该方法就会被调用。</span><br><span class="line"></span><br><span class="line">​		Object.getPrototypeOf， 返回的是参数的原型。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>const handler = {<br>getPrototypeOf(target) {<br>return monsterPrototype;<br>}<br>};</p>
</li>
</ul>
</li>
</ul>
<p>简单来说，返回值就是原型。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### handler.has()</span><br><span class="line"></span><br><span class="line">​		 **`handler.has()`** 方法是针对 [`in`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/in) 操作符的代理方法。</span><br><span class="line"></span><br><span class="line">​		如果指定的属性在指定的对象或其原型链中，则**`in` 运算符**返回`true`。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### handler.isExtensible()</span><br><span class="line"></span><br><span class="line">​		**handler.isExtensible()** 方法用于拦截对对象的Object.isExtensible()。</span><br><span class="line"></span><br><span class="line">​		`**Object.isExtensible()**` 方法判断一个对象是否是可扩展的（是否可以在它上面添加新的属性）。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### handler.ownKeys()</span><br><span class="line"></span><br><span class="line">​		**`handler.ownKeys()`** 方法用于拦截 [`Reflect.ownKeys()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/ownKeys).</span><br><span class="line"></span><br><span class="line">​		静态方法 `**Reflect**`**`.ownKeys()`** 返回一个由目标对象自身的属性键组成的数组。</span><br><span class="line"></span><br><span class="line">描述：</span><br><span class="line"></span><br><span class="line">​		Reflect.ownKeys` 方法返回一个由目标对象自身的属性键组成的数组。它的返回值等同于`Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))。</span><br><span class="line"></span><br><span class="line">​		getOwnPropertyNames：返回自身属性，包括不可枚举，但不包括 Symbol 属性</span><br><span class="line"></span><br><span class="line">​		getOwnPropertySymbols：返回自身的 Symbol 属性</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### handler.preventExtensions()</span><br><span class="line"></span><br><span class="line">​		**`handler.preventExtensions()`** 方法用于设置对[`Object.preventExtensions()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions)的拦截</span><br><span class="line"></span><br><span class="line">​		`**Object.preventExtensions()**`方法让一个对象变的不可扩展，也就是永远不能再添加新的属性。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### handler.set()</span><br><span class="line"></span><br><span class="line">​		`**handler.set()**` 方法是设置属性值操作的捕获器。</span><br><span class="line"></span><br><span class="line">语法：</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>const p = new Proxy(target, {<br>  set: function(target, property, value, receiver) {<br>  }<br>});</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">参数：</span><br><span class="line"></span><br><span class="line">​		以下是传递给 `set()` 方法的参数。`this` 绑定在 handler 对象上。</span><br><span class="line"></span><br><span class="line">* target： 目标对象</span><br><span class="line"></span><br><span class="line">* property： 被设置的属性名或者 Symbol</span><br><span class="line"></span><br><span class="line">* value： 新的属性值</span><br><span class="line"></span><br><span class="line">* receiver： 最初被调用的对象。通常是 proxy 本身，但 handler 的 set 方法也有可能在原型链上，或以其他方式被间接地调用（因此不一定是 proxy 本身）。</span><br><span class="line"></span><br><span class="line">* ```</span><br><span class="line">  const monster1 = &#123; eyeCount: 4 &#125;;</span><br><span class="line">  </span><br><span class="line">  const m = &#123;</span><br><span class="line">    a: 12,</span><br><span class="line">  &#125;</span><br><span class="line">  const handler2 = &#123;</span><br><span class="line">    set(o, p, v, re) &#123;</span><br><span class="line">      // console.log(this)</span><br><span class="line">      // console.log(re)</span><br><span class="line">  </span><br><span class="line">      return true</span><br><span class="line">    &#125;,</span><br><span class="line">    a: 2,</span><br><span class="line">  &#125;</span><br><span class="line">  const pro2 = new Proxy(m, handler2)</span><br><span class="line">  </span><br><span class="line">  monster1.__proto__ = pro2</span><br><span class="line">  </span><br><span class="line">  // ok</span><br><span class="line">  monster1.d = 123</span><br><span class="line">  // not ok</span><br><span class="line">  monster1.b = 321</span><br><span class="line">  monster1.c = 321</span><br><span class="line">  </span><br><span class="line">  console.log(Object.getOwnPropertyNames(monster1), monster1.b)</span><br><span class="line">  console.log(Object.getOwnPropertyNames(pro2), pro2.b)</span><br><span class="line">  </span><br><span class="line">  // 不理解为什么，不行</span><br><span class="line">  // 目前来说，就是 children 的原型的指向是一个 proxy的实例，然后proxy的实例存在一个set方法。然后再 children设置属性的时候，出现了children不含这个属性，所以就会出现，调用proxy实例，方法。但是我返回值为true，但是还是没有赋值成功。但从案例来说，就是在原型修改之前和之后出现的变化。</span><br></pre></td></tr></table></figure>



<h4 id="handler-setPrototypeOf"><a href="#handler-setPrototypeOf" class="headerlink" title="handler.setPrototypeOf()"></a>handler.setPrototypeOf()</h4><p>​        <strong><code>handler.setPrototypeOf()</code></strong> 方法主要用来拦截 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf"><code>Object.setPrototypeOf()</code></a>.</p>
<p>​        <strong>Object.setPrototypeOf()</strong> 方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性）到另一个对象或  <a target="_blank" rel="noopener" href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/null"><code>null</code></a>。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://tallgy.gitee.io/2022/02/18/React/React-Hook/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tallgy">
      <meta itemprop="description" content="只是一个知识的搬运工">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="tallgy's blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/02/18/React/React-Hook/" class="post-title-link" itemprop="url">React-Hook</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-02-18 18:19:50" itemprop="dateCreated datePublished" datetime="2022-02-18T18:19:50+08:00">2022-02-18</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2023-06-25 16:57:29" itemprop="dateModified" datetime="2023-06-25T16:57:29+08:00">2023-06-25</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/React/" itemprop="url" rel="index"><span itemprop="name">React</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="React-Hook"><a href="#React-Hook" class="headerlink" title="React-Hook"></a>React-Hook</h1>
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://tallgy.gitee.io/2022/02/17/React/react-%E9%AB%98%E7%BA%A7%E6%8C%87%E5%BC%95/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tallgy">
      <meta itemprop="description" content="只是一个知识的搬运工">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="tallgy's blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/02/17/React/react-%E9%AB%98%E7%BA%A7%E6%8C%87%E5%BC%95/" class="post-title-link" itemprop="url">react-高级指引</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-02-17 11:00:05" itemprop="dateCreated datePublished" datetime="2022-02-17T11:00:05+08:00">2022-02-17</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2023-06-25 16:57:29" itemprop="dateModified" datetime="2023-06-25T16:57:29+08:00">2023-06-25</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/React/" itemprop="url" rel="index"><span itemprop="name">React</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="React-高级指引"><a href="#React-高级指引" class="headerlink" title="React 高级指引"></a>React 高级指引</h1><h2 id="无障碍"><a href="#无障碍" class="headerlink" title="无障碍"></a>无障碍</h2><h3 id="语义化的-HTML"><a href="#语义化的-HTML" class="headerlink" title="语义化的 HTML"></a>语义化的 HTML</h3><p>​        简单来说，就是如果对于比如 dl dt dd，其中这样的构造。</p>
<p>​        正常的使用 ，就是如同下面这样，因为return需要一个根组件，但是我们有两个标签，所以需要将其用div包含起来。</p>
<p>​        但是这样就会破坏语义性。所以就需要使用一个 Fragment 标签，其语义和template标签可以说是一样的。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">function ListItem(&#123; item &#125;) &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;dt&gt;&#123;item.term&#125;&lt;/dt&gt;</span><br><span class="line">      &lt;dd&gt;&#123;item.description&#125;&lt;/dd&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function Glossary(props) &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;dl&gt;</span><br><span class="line">      &#123;props.items.map(item =&gt; (</span><br><span class="line">        &lt;ListItem item=&#123;item&#125; key=&#123;item.id&#125; /&gt;</span><br><span class="line">      ))&#125;</span><br><span class="line">    &lt;/dl&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">import React, &#123; Fragment &#125; from &#x27;react&#x27;;</span><br><span class="line"></span><br><span class="line">function ListItem(&#123; item &#125;) &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;Fragment&gt;</span><br><span class="line">      &lt;dt&gt;&#123;item.term&#125;&lt;/dt&gt;</span><br><span class="line">      &lt;dd&gt;&#123;item.description&#125;&lt;/dd&gt;</span><br><span class="line">    &lt;/Fragment&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function Glossary(props) &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;dl&gt;</span><br><span class="line">      &#123;props.items.map(item =&gt; (</span><br><span class="line">        &lt;ListItem item=&#123;item&#125; key=&#123;item.id&#125; /&gt;</span><br><span class="line">      ))&#125;</span><br><span class="line">    &lt;/dl&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>​        和其他的元素一样，你可以把一系列的对象映射到一个 fragment 的数组中。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">function Glossary(props) &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;dl&gt;</span><br><span class="line">      &#123;props.items.map(item =&gt; (</span><br><span class="line">        // Fragments should also have a `key` prop when mapping collections</span><br><span class="line">        &lt;Fragment key=&#123;item.id&#125;&gt;          &lt;dt&gt;&#123;item.term&#125;&lt;/dt&gt;</span><br><span class="line">          &lt;dd&gt;&#123;item.description&#125;&lt;/dd&gt;</span><br><span class="line">        &lt;/Fragment&gt;      ))&#125;</span><br><span class="line">    &lt;/dl&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        当你不需要在 fragment 标签中添加任何 prop 且你的工具支持的时候，你可以使用 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/fragments.html#short-syntax">短语法</a>：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">function ListItem(&#123; item &#125;) &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;&gt;      &lt;dt&gt;&#123;item.term&#125;&lt;/dt&gt;</span><br><span class="line">      &lt;dd&gt;&#123;item.description&#125;&lt;/dd&gt;</span><br><span class="line">    &lt;/&gt;  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="控制焦点"><a href="#控制焦点" class="headerlink" title="控制焦点"></a>控制焦点</h3><p>​        我们可以使用 tab 键来进行焦点的切换。其中可以切换的是 a标签，input标签等可以获取focus的标签。</p>
<h3 id="使用程序管理焦点"><a href="#使用程序管理焦点" class="headerlink" title="使用程序管理焦点"></a>使用程序管理焦点</h3><p>​        简单来说，就是我们可以创建一个DOM元素ref，然后将其赋值给了ref。于是就可以通过 this.textInput.current 来获取当前DOM节点。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">class CustomTextInput extends React.Component &#123;</span><br><span class="line">  constructor(props) &#123;</span><br><span class="line">    super(props);</span><br><span class="line">    // 创造一个 textInput DOM 元素的 ref</span><br><span class="line">    this.textInput = React.createRef();</span><br><span class="line">  &#125;</span><br><span class="line">  render() &#123;</span><br><span class="line">  // 使用 `ref` 回调函数以在实例的一个变量中存储文本输入 DOM 元素</span><br><span class="line">  //（比如，this.textInput）。</span><br><span class="line">    return (</span><br><span class="line">      &lt;input</span><br><span class="line">        type=&quot;text&quot;</span><br><span class="line">        ref=&#123;this.textInput&#125;</span><br><span class="line">      /&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        然后我们就可以在需要时于其他地方把焦点设置在这个组件上：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">focus() &#123;</span><br><span class="line">  // 使用原始的 DOM API 显式地聚焦在 text input 上</span><br><span class="line">  // 注意：我们通过访问 “current” 来获得 DOM 节点</span><br><span class="line">  this.textInput.current.focus();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        其大意和vue的ref一样，但是这里我们需要先通过React.createRef 来创建一个ref。然后再将其用ref赋值给标签则可以获得对应的DOM节点。</p>
<p>​        我们需要使用 current 来获取当前的节点。同时我们如果对一个值多次使用ref赋值，只有第一次的会有作用</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">我这里，div ref=&#123;this.textInput&#125;</span><br><span class="line">			CustomInput ref=&#123;this.textInput&#125;</span><br><span class="line">其最后的结果就是 div，current就只有这一个对象，并没有生成对应的多个对象出来。</span><br><span class="line"></span><br><span class="line">class App extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    private textInput : any</span><br><span class="line"></span><br><span class="line">    constructor(props : any) &#123;</span><br><span class="line">        super(props);</span><br><span class="line"></span><br><span class="line">        this.textInput = React.createRef();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line"></span><br><span class="line">    return (</span><br><span class="line">        &lt;div ref=&#123;this.textInput&#125;&gt;</span><br><span class="line">            &lt;CustomInput ref=&#123;this.textInput&#125; inputRef=&#123;this.textInput&#125;&gt;&lt;/CustomInput&gt;</span><br><span class="line">        &lt;/div&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        通过暴露ref，来让父组件的ref传向子节点。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">function CustomTextInput(props) &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;input ref=&#123;props.inputRef&#125; /&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class Parent extends React.Component &#123;</span><br><span class="line">  constructor(props) &#123;</span><br><span class="line">    super(props);</span><br><span class="line">    this.inputElement = React.createRef();</span><br><span class="line">  &#125;</span><br><span class="line">  render() &#123;</span><br><span class="line">    return (</span><br><span class="line">      &lt;CustomTextInput inputRef=&#123;this.inputElement&#125; /&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 现在你就可以在需要时设置焦点了</span><br><span class="line">this.inputElement.current.focus();</span><br></pre></td></tr></table></figure>



<img src="/.io//image-20220217140334480.png" alt="image-20220217140334480" style="zoom:67%;">

<p>​        通过点击事件来让 ul 进行显示和隐藏，一般的想法就是使用click事件，设置一个Boolean值来进行显示和隐藏。</p>
<p>​        但是这样对于使用键盘的就不会过于的友好。因为这样点击事件启动之后，就需要其他的点击事件来进行操作会隐藏当前 ul 标签。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">button的点击事件</span><br><span class="line">onClickHandler() &#123;</span><br><span class="line">    this.setState(currentState =&gt; (&#123;</span><br><span class="line">      isOpen: !currentState.isOpen</span><br><span class="line">    &#125;));</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">window对象监听点击事件。通过判断点击事件是否属于toggleContariner元素来确定是否需要隐藏。</span><br><span class="line">  onClickOutsideHandler(event) &#123;</span><br><span class="line">    if (this.state.isOpen &amp;&amp; !this.toggleContainer.current.contains(event.target)) &#123;</span><br><span class="line">      this.setState(&#123; isOpen: false &#125;);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>​        所以我们可以使用 onBlur 和 onFocus 来完成这个效果。</p>
<p>​        我的思考是，直接添加 onBlur 和 onFocus 来进行判断，虽然这样对于button的点击事件就会一直处于focus状态。</p>
<p>​        官网的思路就是，click事件还是添加在button上面，然后使用focus和blur来进行事件的处理。但是这里存在一个问题，那就是对于focus和blur的事件需要有能够进行事件的才会使用，但是官网案例的显示是ul标签，不存在焦点事件，所以出现了问题。</p>
<p>​        通过官网并不是获取焦点就直接为true，而是使用了 settimeout方法。在get focus的时候清除定时器。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line">class BlurExample extends React.Component &#123;</span><br><span class="line">  constructor(props) &#123;</span><br><span class="line">    super(props);</span><br><span class="line"></span><br><span class="line">    this.state = &#123; isOpen: false &#125;;</span><br><span class="line">    this.timeOutId = null;</span><br><span class="line"></span><br><span class="line">    this.onClickHandler = this.onClickHandler.bind(this);</span><br><span class="line">    this.onBlurHandler = this.onBlurHandler.bind(this);</span><br><span class="line">    this.onFocusHandler = this.onFocusHandler.bind(this);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  onClickHandler() &#123;</span><br><span class="line">    this.setState(currentState =&gt; (&#123;</span><br><span class="line">      isOpen: !currentState.isOpen</span><br><span class="line">    &#125;));</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  // 我们在下一个时间点使用 setTimeout 关闭弹窗。</span><br><span class="line">  // 这是必要的，因为失去焦点事件会在新的焦点事件前被触发，</span><br><span class="line">  // 我们需要通过这个步骤确认这个元素的一个子节点</span><br><span class="line">  // 是否得到了焦点。</span><br><span class="line">  onBlurHandler() &#123;</span><br><span class="line">    this.timeOutId = setTimeout(() =&gt; &#123;</span><br><span class="line">      this.setState(&#123;</span><br><span class="line">        isOpen: false</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  // 如果一个子节点获得了焦点，不要关闭弹窗。</span><br><span class="line">  onFocusHandler() &#123;</span><br><span class="line">    clearTimeout(this.timeOutId);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    // React 通过把失去焦点和获得焦点事件传输给父节点</span><br><span class="line">    // 来帮助我们。</span><br><span class="line">    return (</span><br><span class="line">      &lt;div onBlur=&#123;this.onBlurHandler&#125;</span><br><span class="line">           onFocus=&#123;this.onFocusHandler&#125;&gt;</span><br><span class="line">        &lt;button onClick=&#123;this.onClickHandler&#125;</span><br><span class="line">                aria-haspopup=&quot;true&quot;</span><br><span class="line">                aria-expanded=&#123;this.state.isOpen&#125;&gt;</span><br><span class="line">          Select an option</span><br><span class="line">        &lt;/button&gt;</span><br><span class="line">        &#123;this.state.isOpen &amp;&amp; (</span><br><span class="line">          &lt;ul&gt;</span><br><span class="line">            &lt;li&gt;Option 1&lt;/li&gt;</span><br><span class="line">            &lt;li&gt;Option 2&lt;/li&gt;</span><br><span class="line">            &lt;li&gt;Option 3&lt;/li&gt;</span><br><span class="line">          &lt;/ul&gt;</span><br><span class="line">        )&#125;</span><br><span class="line">      &lt;/div&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="开发辅助"><a href="#开发辅助" class="headerlink" title="开发辅助"></a>开发辅助</h3><p>​        我们可以直接在 JSX 代码中检测一些无障碍复制功能。通常支持 JSX 的 IDE 会针对 ARIA roles，states 和 properties 提供智能检测。我们也可以使用以下工具：</p>
<h4 id="eslint-plugin-jsx-a11y"><a href="#eslint-plugin-jsx-a11y" class="headerlink" title="eslint-plugin-jsx-a11y"></a>eslint-plugin-jsx-a11y</h4><p>​        ESLint 中的 <a target="_blank" rel="noopener" href="https://github.com/evcohen/eslint-plugin-jsx-a11y">eslint-plugin-jsx-a11y</a> 插件为你的 JSX 中的无障碍问题提供了 AST 的语法检测反馈。许多 IDE 都允许你把这些发现直接集成到代码分析和源文件窗口中。</p>
<p>​         <a target="_blank" rel="noopener" href="https://github.com/facebookincubator/create-react-app">Create React App</a>中使用了这个插件中的一部分规则。如果你想启用更多的无障碍规则，你可以在项目的根目录中创建一个有如下内容的 <code>.eslintrc</code> 文件：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  &quot;extends&quot;: [&quot;react-app&quot;, &quot;plugin:jsx-a11y/recommended&quot;],</span><br><span class="line">  &quot;plugins&quot;: [&quot;jsx-a11y&quot;]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id><a href="#" class="headerlink" title></a></h3><h2 id="代码分割"><a href="#代码分割" class="headerlink" title="代码分割"></a>代码分割</h2><p>​        为了避免搞出大体积的代码包，在前期就思考该问题并对代码包进行分割是个不错的选择。 代码分割是由诸如 <a target="_blank" rel="noopener" href="https://webpack.docschina.org/guides/code-splitting/">Webpack</a>，<a target="_blank" rel="noopener" href="https://rollupjs.org/guide/en/#code-splitting">Rollup</a> 和 Browserify（<a target="_blank" rel="noopener" href="https://github.com/browserify/factor-bundle">factor-bundle</a>）这类打包器支持的一项技术，能够创建多个包并在运行时动态加载。</p>
<p>​        对你的应用进行代码分割能够帮助你“懒加载”当前用户所需要的内容，能够显著地提高你的应用性能。尽管并没有减少应用整体的代码体积，但你可以避免加载用户永远不需要的代码，并在初始加载的时候减少所需加载的代码量。</p>
<h3 id="import"><a href="#import" class="headerlink" title="import()"></a>import()</h3><p>​        在你的应用中引入代码分割的最佳方式是通过动态 <code>import()</code> 语法。</p>
<p>​        <strong>使用之前：</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">import &#123; add &#125; from &#x27;./math&#x27;;</span><br><span class="line"></span><br><span class="line">console.log(add(16, 26));</span><br></pre></td></tr></table></figure>

<p>​        <strong>使用之后：</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">import(&quot;./math&quot;).then(math =&gt; &#123;</span><br><span class="line">  console.log(math.add(16, 26));</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>​        当 Webpack 解析到该语法时，会自动进行代码分割。如果你使用 Create React App，该功能已开箱即用，你可以<a target="_blank" rel="noopener" href="https://create-react-app.dev/docs/code-splitting/">立刻使用</a>该特性。<a target="_blank" rel="noopener" href="https://nextjs.org/docs/advanced-features/dynamic-import">Next.js</a> 也已支持该特性而无需进行配置。</p>
<p>​        如果你自己配置 Webpack，你可能要阅读下 Webpack 关于<a target="_blank" rel="noopener" href="https://webpack.docschina.org/guides/code-splitting/">代码分割</a>的指南。你的 Webpack 配置应该<a target="_blank" rel="noopener" href="https://gist.github.com/gaearon/ca6e803f5c604d37468b0091d9959269">类似于此</a>。</p>
<p>​        当使用 <a target="_blank" rel="noopener" href="https://babeljs.io/">Babel</a> 时，你要确保 Babel 能够解析动态 import 语法而不是将其进行转换。对于这一要求你需要 <a target="_blank" rel="noopener" href="https://classic.yarnpkg.com/en/package/@babel/plugin-syntax-dynamic-import">@babel/plugin-syntax-dynamic-import</a> 插件。</p>
<h3 id="React-lazy"><a href="#React-lazy" class="headerlink" title="React.lazy"></a>React.lazy</h3><blockquote>
<p>注意:</p>
<p><code>React.lazy</code> 和 Suspense 技术还不支持服务端渲染。如果你想要在使用服务端渲染的应用中使用，我们推荐 <a target="_blank" rel="noopener" href="https://github.com/gregberge/loadable-components">Loadable Components</a> 这个库。它有一个很棒的<a target="_blank" rel="noopener" href="https://loadable-components.com/docs/server-side-rendering/">服务端渲染打包指南</a>。</p>
</blockquote>
<p><strong>使用之前：</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">import OtherComponent from &#x27;./OtherComponent&#x27;;</span><br></pre></td></tr></table></figure>

<p><strong>使用之后：</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const OtherComponent = React.lazy(() =&gt; import(&#x27;./OtherComponent&#x27;));</span><br></pre></td></tr></table></figure>

<p>​        此代码将会在组件首次渲染时，自动导入包含 <code>OtherComponent</code> 组件的包。</p>
<p>​        <code>React.lazy</code> 接受一个函数，这个函数需要动态调用 <code>import()</code>。它必须返回一个 <code>Promise</code>，该 Promise 需要 resolve 一个 <code>default</code> export 的 React 组件。</p>
<p>​        然后应在 <code>Suspense</code> 组件中渲染 lazy 组件，如此使得我们可以使用在等待加载 lazy 组件时做优雅降级（如 loading 指示器等）。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">import React, &#123; Suspense &#125; from &#x27;react&#x27;;</span><br><span class="line"></span><br><span class="line">const OtherComponent = React.lazy(() =&gt; import(&#x27;./OtherComponent&#x27;));</span><br><span class="line"></span><br><span class="line">function MyComponent() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;Suspense fallback=&#123;&lt;div&gt;Loading...&lt;/div&gt;&#125;&gt;</span><br><span class="line">        &lt;OtherComponent /&gt;</span><br><span class="line">      &lt;/Suspense&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        主要有几个点：</p>
<ul>
<li>React.lazy() 进行promise加载</li>
<li>suspense 使用 里面的 fallback 来进行 加载过程中的优雅降级。</li>
</ul>
<p>​        <code>fallback</code> 属性接受任何在组件加载过程中你想展示的 React 元素。你可以将 <code>Suspense</code> 组件置于懒加载组件之上的任何位置。你甚至可以用一个 <code>Suspense</code> 组件包裹多个懒加载组件。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">import React, &#123; Suspense &#125; from &#x27;react&#x27;;</span><br><span class="line"></span><br><span class="line">const OtherComponent = React.lazy(() =&gt; import(&#x27;./OtherComponent&#x27;));</span><br><span class="line">const AnotherComponent = React.lazy(() =&gt; import(&#x27;./AnotherComponent&#x27;));</span><br><span class="line"></span><br><span class="line">function MyComponent() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;Suspense fallback=&#123;&lt;div&gt;Loading...&lt;/div&gt;&#125;&gt;</span><br><span class="line">        &lt;section&gt;</span><br><span class="line">          &lt;OtherComponent /&gt;</span><br><span class="line">          &lt;AnotherComponent /&gt;</span><br><span class="line">        &lt;/section&gt;</span><br><span class="line">      &lt;/Suspense&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="异常捕获边界"><a href="#异常捕获边界" class="headerlink" title="异常捕获边界"></a>异常捕获边界</h3><p>​        如果模块加载失败（如网络问题），它会触发一个错误。你可以通过<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/error-boundaries.html">异常捕获边界（Error boundaries）</a>技术来处理这些情况，以显示良好的用户体验并管理恢复事宜。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">import React, &#123; Suspense &#125; from &#x27;react&#x27;;</span><br><span class="line">import MyErrorBoundary from &#x27;./MyErrorBoundary&#x27;;</span><br><span class="line"></span><br><span class="line">const OtherComponent = React.lazy(() =&gt; import(&#x27;./OtherComponent&#x27;));</span><br><span class="line">const AnotherComponent = React.lazy(() =&gt; import(&#x27;./AnotherComponent&#x27;));</span><br><span class="line"></span><br><span class="line">const MyComponent = () =&gt; (</span><br><span class="line">  &lt;div&gt;</span><br><span class="line">    &lt;MyErrorBoundary&gt;</span><br><span class="line">      &lt;Suspense fallback=&#123;&lt;div&gt;Loading...&lt;/div&gt;&#125;&gt;</span><br><span class="line">        &lt;section&gt;</span><br><span class="line">          &lt;OtherComponent /&gt;</span><br><span class="line">          &lt;AnotherComponent /&gt;</span><br><span class="line">        &lt;/section&gt;</span><br><span class="line">      &lt;/Suspense&gt;</span><br><span class="line">    &lt;/MyErrorBoundary&gt;</span><br><span class="line">  &lt;/div&gt;</span><br><span class="line">);</span><br></pre></td></tr></table></figure>

<p>​        具体的后续会说。</p>
<h3 id="基于路由的代码分割"><a href="#基于路由的代码分割" class="headerlink" title="基于路由的代码分割"></a>基于路由的代码分割</h3><p>​        这里是一个例子，展示如何在你的应用中使用 <code>React.lazy</code> 和 <a target="_blank" rel="noopener" href="https://reactrouter.com/">React Router</a> 这类的第三方库，来配置基于路由的代码分割。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">import React, &#123; Suspense, lazy &#125; from &#x27;react&#x27;;</span><br><span class="line">import &#123; BrowserRouter as Router, Routes, Route &#125; from &#x27;react-router-dom&#x27;;</span><br><span class="line"></span><br><span class="line">const Home = lazy(() =&gt; import(&#x27;./routes/Home&#x27;));</span><br><span class="line">const About = lazy(() =&gt; import(&#x27;./routes/About&#x27;));</span><br><span class="line"></span><br><span class="line">const App = () =&gt; (</span><br><span class="line">  &lt;Router&gt;</span><br><span class="line">    &lt;Suspense fallback=&#123;&lt;div&gt;Loading...&lt;/div&gt;&#125;&gt;</span><br><span class="line">      &lt;Routes&gt;</span><br><span class="line">        &lt;Route path=&quot;/&quot; element=&#123;&lt;Home /&gt;&#125; /&gt;</span><br><span class="line">        &lt;Route path=&quot;/about&quot; element=&#123;&lt;About /&gt;&#125; /&gt;</span><br><span class="line">      &lt;/Routes&gt;</span><br><span class="line">    &lt;/Suspense&gt;</span><br><span class="line">  &lt;/Router&gt;</span><br><span class="line">);</span><br></pre></td></tr></table></figure>



<h3 id="命名导出"><a href="#命名导出" class="headerlink" title="命名导出"></a>命名导出</h3><p>​        简单来说就是使用 React.lazy 方法。他只会使用默认导出。</p>
<p>​        <code>React.lazy</code> 目前只支持默认导出（default exports）。如果你想被引入的模块使用命名导出（named exports），你可以创建一个中间模块，来重新导出为默认模块。这能保证 tree shaking 不会出错，并且不必引入不需要的组件。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">// ManyComponents.js</span><br><span class="line">export const MyComponent = /* ... */;</span><br><span class="line">export const MyUnusedComponent = /* ... */;</span><br><span class="line"></span><br><span class="line">// MyComponent.js</span><br><span class="line">export &#123; MyComponent as default &#125; from &quot;./ManyComponents.js&quot;;</span><br><span class="line"></span><br><span class="line">// MyApp.js</span><br><span class="line">import React, &#123; lazy &#125; from &#x27;react&#x27;;</span><br><span class="line">const MyComponent = lazy(() =&gt; import(&quot;./MyComponent.js&quot;));</span><br></pre></td></tr></table></figure>



<h2 id="Context"><a href="#Context" class="headerlink" title="Context"></a>Context</h2><p>​        Context 提供了一个无需为每层组件手动添加 props，就能在组件树间进行数据传递的方法。</p>
<p>​        简单来说就是，因为props的是需要通过组件进行传递，那么此时，如果我们需要传递的属性极其繁琐。所以就会导致很麻烦，此时就可以使用context的形式。</p>
<p>​    <strong>例子：</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">class ThemeButton extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">        return &lt;button&gt;&#123;this.props.theme&#125;&lt;/button&gt;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class Toolbar extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">        return &lt;ThemeButton theme=&#123;this.props.theme&#125;&gt;&lt;/ThemeButton&gt;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class App extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">        return (</span><br><span class="line">            &lt;div&gt;</span><br><span class="line">                &lt;input type=&quot;text&quot;/&gt;</span><br><span class="line">                &lt;Toolbar theme=&#123;&#x27;qqq&#x27;&#125;&gt;&lt;/Toolbar&gt;</span><br><span class="line">            &lt;/div&gt;</span><br><span class="line">        );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        我们可以发现，App为了传递一个theme给ThemeButton ，从中间传递了几次，类似这种，传递次数又长，又冗余的。</p>
<p>​        使用 context 进行传递。</p>
<ul>
<li>首先，需要使用 const ThemeContext = React.createContext(‘light’); 进行创建一个context</li>
<li>然后，对于要传递的地方。使用 &lt;ThemeContext.Provider value=”dark”&gt; xxx &lt;/ThemeContext.Provider&gt; 标签进行包裹。这里的ThemeContext 就是创建context的命名。这里的 provider 就是代表了会将value进行传递给以下的组件树。</li>
<li>然后中间组件不需要传递 </li>
<li>对于要使用的，我们直接创建一个 static contextType = ThemeContext; 这里 contextType 是固定的。不可修改的。然后只需要再在标签里面通过 this.context 就可以访问了。</li>
<li>虽然感觉很奇怪。。</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">// Context 可以让我们无须明确地传遍每一个组件，就能将值深入传递进组件树。</span><br><span class="line">// 为当前的 theme 创建一个 context（“light”为默认值）。</span><br><span class="line">const ThemeContext = React.createContext(&#x27;light&#x27;);</span><br><span class="line">class App extends React.Component &#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。</span><br><span class="line">    // 无论多深，任何组件都能读取这个值。</span><br><span class="line">    // 在这个例子中，我们将 “dark” 作为当前的值传递下去。</span><br><span class="line">    return (</span><br><span class="line">      &lt;ThemeContext.Provider value=&quot;dark&quot;&gt;</span><br><span class="line">        &lt;Toolbar /&gt;</span><br><span class="line">      &lt;/ThemeContext.Provider&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 中间的组件再也不必指明往下传递 theme 了。</span><br><span class="line">function Toolbar() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;ThemedButton /&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class ThemedButton extends React.Component &#123;</span><br><span class="line">  // 指定 contextType 读取当前的 theme context。</span><br><span class="line">  // React 会往上找到最近的 theme Provider，然后使用它的值。</span><br><span class="line">  // 在这个例子中，当前的 theme 值为 “dark”。</span><br><span class="line">  static contextType = ThemeContext;</span><br><span class="line">  render() &#123;</span><br><span class="line">    return &lt;Button theme=&#123;this.context&#125; /&gt;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>​        <strong>如果你只是想避免层层传递一些属性，<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/composition-vs-inheritance.html">组件组合（component composition）</a>有时候是一个比 context 更好的解决方案。</strong></p>
<p>​        一种 <strong>无需 context</strong> 的解决方案是<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/composition-vs-inheritance.html#containment">将 <code>Avatar</code> 组件自身传递下去</a>，因为中间组件无需知道 <code>user</code> 或者 <code>avatarSize</code> 等 props：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">function Page(props) &#123;</span><br><span class="line">  const user = props.user;</span><br><span class="line">  const userLink = (</span><br><span class="line">    &lt;Link href=&#123;user.permalink&#125;&gt;</span><br><span class="line">      &lt;Avatar user=&#123;user&#125; size=&#123;props.avatarSize&#125; /&gt;</span><br><span class="line">    &lt;/Link&gt;</span><br><span class="line">  );</span><br><span class="line">  return &lt;PageLayout userLink=&#123;userLink&#125; /&gt;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 现在，我们有这样的组件：</span><br><span class="line">&lt;Page user=&#123;user&#125; avatarSize=&#123;avatarSize&#125; /&gt;</span><br><span class="line">// ... 渲染出 ...</span><br><span class="line">&lt;PageLayout userLink=&#123;...&#125; /&gt;</span><br><span class="line">// ... 渲染出 ...</span><br><span class="line">&lt;NavigationBar userLink=&#123;...&#125; /&gt;</span><br><span class="line">// ... 渲染出 ...</span><br><span class="line">&#123;props.userLink&#125;</span><br></pre></td></tr></table></figure>

<p>​        简单来说就是，我们传递的时候，将组件传递过去，然后中间层，只需要传递组件props，而不会传递其他的，就轻松很多，如同一种插槽一样的感觉。</p>
<h3 id="React-createContext-API"><a href="#React-createContext-API" class="headerlink" title="React.createContext API"></a><code>React.createContext</code> API</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const MyContext = React.createContext(defaultValue);</span><br></pre></td></tr></table></figure>

<p>​        创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件，这个组件会从组件树中离自身最近的那个匹配的 <code>Provider</code> 中读取到当前的 context 值。</p>
<p>​        <strong>只有</strong>当组件所处的树中没有匹配到 Provider 时，其 <code>defaultValue</code> 参数才会生效。此默认值有助于在不使用 Provider 包装组件的情况下对组件进行测试。注意：将 <code>undefined</code> 传递给 Provider 的 value 时，消费组件的 <code>defaultValue</code> 不会生效。</p>
<p>​        具体的查看官方文档进行了解。</p>
<h2 id="错误边界"><a href="#错误边界" class="headerlink" title="错误边界"></a>错误边界</h2><p>​        错误边界是一种 React 组件，这种组件<strong>可以捕获发生在其子组件树任何位置的 JavaScript 错误，并打印这些错误，同时展示降级 UI</strong>，而并不会渲染那些发生崩溃的子组件树。错误边界可以捕获发生在整个子组件树的渲染期间、生命周期方法以及构造函数中的错误。</p>
<blockquote>
<p>注意</p>
<p>错误边界<strong>无法</strong>捕获以下场景中产生的错误：</p>
<ul>
<li>事件处理（<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/error-boundaries.html#how-about-event-handlers">了解更多</a>）</li>
<li>异步代码（例如 <code>setTimeout</code> 或 <code>requestAnimationFrame</code> 回调函数）</li>
<li>服务端渲染</li>
<li>它自身抛出来的错误（并非它的子组件）</li>
</ul>
</blockquote>
<p>​        如果一个 class 组件中定义了 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/react-component.html#static-getderivedstatefromerror"><code>static getDerivedStateFromError()</code></a> 或 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/react-component.html#componentdidcatch"><code>componentDidCatch()</code></a> 这两个生命周期方法中的任意一个（或两个）时，那么它就变成一个错误边界。当抛出错误后，请使用 <code>static getDerivedStateFromError()</code> 渲染备用 UI ，使用 <code>componentDidCatch()</code> 打印错误信息。</p>
<ul>
<li>static getDerivedStateFromError()<ul>
<li>渲染备用UI</li>
</ul>
</li>
<li>componentDidCatch()<ul>
<li>打印错误信息</li>
</ul>
</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">class ErrorBoundary extends React.Component &#123;</span><br><span class="line">  constructor(props) &#123;</span><br><span class="line">    super(props);</span><br><span class="line">    this.state = &#123; hasError: false &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  static getDerivedStateFromError(error) &#123;</span><br><span class="line">    // 更新 state 使下一次渲染能够显示降级后的 UI</span><br><span class="line">    return &#123; hasError: true &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  componentDidCatch(error, errorInfo) &#123;</span><br><span class="line">    // 你同样可以将错误日志上报给服务器</span><br><span class="line">    logErrorToMyService(error, errorInfo);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    if (this.state.hasError) &#123;</span><br><span class="line">      // 你可以自定义降级后的 UI 并渲染</span><br><span class="line">      return &lt;h1&gt;Something went wrong.&lt;/h1&gt;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    return this.props.children; </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        然后你可以将它作为一个常规组件去使用：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;ErrorBoundary&gt;</span><br><span class="line">  &lt;MyWidget /&gt;</span><br><span class="line">&lt;/ErrorBoundary&gt;</span><br></pre></td></tr></table></figure>

<p>​        错误边界的工作方式类似于 JavaScript 的 <code>catch &#123;&#125;</code>，不同的地方在于错误边界只针对 React 组件。只有 class 组件才可以成为错误边界组件。大多数情况下, 你只需要声明一次错误边界组件, 并在整个应用中使用它。</p>
<p>​        注意<strong>错误边界仅可以捕获其子组件的错误</strong>，它无法捕获其自身的错误。如果一个错误边界无法渲染错误信息，则错误会冒泡至最近的上层错误边界，这也类似于 JavaScript 中 <code>catch &#123;&#125;</code> 的工作机制。</p>
<h3 id="未捕获错误的新行为"><a href="#未捕获错误的新行为" class="headerlink" title="未捕获错误的新行为"></a>未捕获错误的新行为</h3><p>​        这一改变具有重要意义，<strong>自 React 16 起，任何未被错误边界捕获的错误将会导致整个 React 组件树被卸载。</strong></p>
<h3 id="组件栈追踪"><a href="#组件栈追踪" class="headerlink" title="组件栈追踪"></a>组件栈追踪</h3><blockquote>
<p>注意</p>
<p>组件名称在栈追踪中的显示依赖于 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name"><code>Function.name</code></a> 属性。如果你想要支持尚未提供该功能的旧版浏览器和设备（例如 IE 11），考虑在你的打包（bundled）应用程序中包含一个 <code>Function.name</code> 的 polyfill，如 <a target="_blank" rel="noopener" href="https://github.com/JamesMGreene/Function.name"><code>function.name-polyfill</code></a> 。或者，你可以在所有组件上显式设置 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/react-component.html#displayname"><code>displayName</code></a> 属性。</p>
</blockquote>
<h2 id="refs转发"><a href="#refs转发" class="headerlink" title="refs转发"></a>refs转发</h2><p>​        Ref 转发是一项将 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/refs-and-the-dom.html">ref</a> 自动地通过组件传递到其一子组件的技巧。对于大多数应用中的组件来说，这通常不是必需的。但其对某些组件，尤其是可重用的组件库是很有用的。最常见的案例如下所述。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">class App extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">        const FancyButton = React.forwardRef((props, ref) =&gt; (</span><br><span class="line">            &lt;button ref=&#123;ref as any&#125; className=&quot;FancyButton&quot;&gt;</span><br><span class="line">                &#123;props.children&#125;</span><br><span class="line">            &lt;/button&gt;</span><br><span class="line">        )) as any;</span><br><span class="line"></span><br><span class="line">        // 你可以直接获取 DOM button 的 ref：</span><br><span class="line">        const ref = React.createRef();</span><br><span class="line">        return &lt;FancyButton ref=&#123;ref&#125;&gt;Click me!&lt;/FancyButton&gt;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="Fragments"><a href="#Fragments" class="headerlink" title="Fragments"></a>Fragments</h2><p>​        React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组，而无需向 DOM 添加额外节点。</p>
<p>​        还有一种新的<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/fragments.html#short-syntax">短语法</a>可用于声明它们。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">render() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;React.Fragment&gt;</span><br><span class="line">      &lt;ChildA /&gt;</span><br><span class="line">      &lt;ChildB /&gt;</span><br><span class="line">      &lt;ChildC /&gt;</span><br><span class="line">    &lt;/React.Fragment&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">render() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;&gt;</span><br><span class="line">      &lt;ChildA /&gt;</span><br><span class="line">      &lt;ChildB /&gt;</span><br><span class="line">      &lt;ChildC /&gt;</span><br><span class="line">    &lt;/&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        作用，就是可以在使用的时候不会影响到DOM树，因为他不会渲染成DOM树。和template相似。</p>
<p>​        同时，可以携带key值，用于循环。</p>
<h2 id="高阶组件"><a href="#高阶组件" class="headerlink" title="高阶组件"></a>高阶组件</h2><p>​        高阶组件（HOC）是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分，它是一种基于 React 的组合特性而形成的设计模式。</p>
<p>​        具体而言，<strong>高阶组件是参数为组件，返回值为新组件的函数。</strong></p>
<p>​        同时高阶组件是一个纯函数。不会修改原来的参数。</p>
<p>​        <code>CommentList</code> 和 <code>BlogPost</code> 不同 - 它们在 <code>DataSource</code> 上调用不同的方法，且渲染不同的结果。但它们的大部分实现都是一样的：</p>
<ul>
<li>在挂载时，向 <code>DataSource</code> 添加一个更改侦听器。</li>
<li>在侦听器内部，当数据源发生变化时，调用 <code>setState</code>。</li>
<li>在卸载时，删除侦听器。</li>
</ul>
<p>​        你可以想象，在一个大型应用程序中，这种订阅 <code>DataSource</code> 和调用 <code>setState</code> 的模式将一次又一次地发生。我们需要一个抽象，允许我们在一个地方定义这个逻辑，并在许多组件之间共享它。这正是高阶组件擅长的地方。</p>
<p>​        对于订阅了 <code>DataSource</code> 的组件，比如 <code>CommentList</code> 和 <code>BlogPost</code>，我们可以编写一个创建组件函数。该函数将接受一个子组件作为它的其中一个参数，该子组件将订阅数据作为 prop。让我们调用函数 <code>withSubscription</code>：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">const CommentListWithSubscription = withSubscription(</span><br><span class="line">  CommentList,</span><br><span class="line">  (DataSource) =&gt; DataSource.getComments()</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">const BlogPostWithSubscription = withSubscription(</span><br><span class="line">  BlogPost,</span><br><span class="line">  (DataSource, props) =&gt; DataSource.getBlogPost(props.id)</span><br><span class="line">);</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">// 此函数接收一个组件...</span><br><span class="line">function withSubscription(WrappedComponent, selectData) &#123;</span><br><span class="line">  // ...并返回另一个组件...</span><br><span class="line">  return class extends React.Component &#123;</span><br><span class="line">    constructor(props) &#123;</span><br><span class="line">      super(props);</span><br><span class="line">      this.handleChange = this.handleChange.bind(this);</span><br><span class="line">      this.state = &#123;</span><br><span class="line">        data: selectData(DataSource, props)</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    componentDidMount() &#123;</span><br><span class="line">      // ...负责订阅相关的操作...</span><br><span class="line">      DataSource.addChangeListener(this.handleChange);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    componentWillUnmount() &#123;</span><br><span class="line">      DataSource.removeChangeListener(this.handleChange);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    handleChange() &#123;</span><br><span class="line">      this.setState(&#123;</span><br><span class="line">        data: selectData(DataSource, this.props)</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line">      // ... 并使用新数据渲染被包装的组件!</span><br><span class="line">      // 请注意，我们可能还会传递其他属性</span><br><span class="line">      return &lt;WrappedComponent data=&#123;this.state.data&#125; &#123;...this.props&#125; /&gt;;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="深入JSX"><a href="#深入JSX" class="headerlink" title="深入JSX"></a>深入JSX</h2><p>​        实际上，JSX 仅仅只是 <code>React.createElement(component, props, ...children)</code> 函数的语法糖。如下 JSX 代码：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;MyButton color=&quot;blue&quot; shadowSize=&#123;2&#125;&gt;</span><br><span class="line">  Click Me</span><br><span class="line">&lt;/MyButton&gt;</span><br></pre></td></tr></table></figure>

<p>会编译为：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">React.createElement(</span><br><span class="line">  MyButton,</span><br><span class="line">  &#123;color: &#x27;blue&#x27;, shadowSize: 2&#125;,</span><br><span class="line">  &#x27;Click Me&#x27;</span><br><span class="line">)</span><br></pre></td></tr></table></figure>

<h3 id="指定-React-元素类型"><a href="#指定-React-元素类型" class="headerlink" title="指定 React 元素类型"></a>指定 React 元素类型</h3><p>​        JSX 标签的第一部分指定了 React 元素的类型。</p>
<p>​        大写字母开头的 JSX 标签意味着它们是 React 组件。这些标签会被编译为对命名变量的直接引用，所以，当你使用 JSX <code>&lt;Foo /&gt;</code> 表达式时，<code>Foo</code> 必须包含在作用域内。</p>
<h4 id="React-必须在作用域内"><a href="#React-必须在作用域内" class="headerlink" title="React 必须在作用域内"></a>React 必须在作用域内</h4><p>​        由于 JSX 会编译为 <code>React.createElement</code> 调用形式，所以 <code>React</code> 库也必须包含在 JSX 代码作用域内。</p>
<p>​        例如，在如下代码中，虽然 <code>React</code> 和 <code>CustomButton</code> 并没有被直接使用，但还是需要导入：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">import React from &#x27;react&#x27;;import CustomButton from &#x27;./CustomButton&#x27;;</span><br><span class="line">function WarningButton() &#123;</span><br><span class="line">  // return React.createElement(CustomButton, &#123;color: &#x27;red&#x27;&#125;, null);  return &lt;CustomButton color=&quot;red&quot; /&gt;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="在-JSX-类型中使用点语法"><a href="#在-JSX-类型中使用点语法" class="headerlink" title="在 JSX 类型中使用点语法"></a>在 JSX 类型中使用点语法</h4><p>​        在 JSX 中，你也可以使用点语法来引用一个 React 组件。当你在一个模块中导出许多 React 组件时，这会非常方便。例如，如果 <code>MyComponents.DatePicker</code> 是一个组件，你可以在 JSX 中直接使用：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">import React from &#x27;react&#x27;;</span><br><span class="line"></span><br><span class="line">const MyComponents = &#123;</span><br><span class="line">  DatePicker: function DatePicker(props) &#123;</span><br><span class="line">    return &lt;div&gt;Imagine a &#123;props.color&#125; datepicker here.&lt;/div&gt;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function BlueDatePicker() &#123;</span><br><span class="line">  return &lt;MyComponents.DatePicker color=&quot;blue&quot; /&gt;;&#125;</span><br></pre></td></tr></table></figure>

<h4 id="用户定义的组件必须以大写字母开头"><a href="#用户定义的组件必须以大写字母开头" class="headerlink" title="用户定义的组件必须以大写字母开头"></a>用户定义的组件必须以大写字母开头</h4><h4 id="在运行时选择类型"><a href="#在运行时选择类型" class="headerlink" title="在运行时选择类型"></a>在运行时选择类型</h4><p>​        你不能将通用表达式作为 React 元素类型。如果你想通过通用表达式来（动态）决定元素类型，你需要首先将它赋值给大写字母开头的变量。这通常用于根据 prop 来渲染不同组件的情况下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">import React from &#x27;react&#x27;;</span><br><span class="line">import &#123; PhotoStory, VideoStory &#125; from &#x27;./stories&#x27;;</span><br><span class="line"></span><br><span class="line">const components = &#123;</span><br><span class="line">  photo: PhotoStory,</span><br><span class="line">  video: VideoStory</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">function Story(props) &#123;</span><br><span class="line">  // 错误！JSX 类型不能是一个表达式。  return &lt;components[props.storyType] story=&#123;props.story&#125; /&gt;;&#125;</span><br></pre></td></tr></table></figure>

<p>​        要解决这个问题, 需要首先将类型赋值给一个大写字母开头的变量：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">import React from &#x27;react&#x27;;</span><br><span class="line">import &#123; PhotoStory, VideoStory &#125; from &#x27;./stories&#x27;;</span><br><span class="line"></span><br><span class="line">const components = &#123;</span><br><span class="line">  photo: PhotoStory,</span><br><span class="line">  video: VideoStory</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">function Story(props) &#123;</span><br><span class="line">  // 正确！JSX 类型可以是大写字母开头的变量。  const SpecificStory = components[props.storyType];  return &lt;SpecificStory story=&#123;props.story&#125; /&gt;;&#125;</span><br></pre></td></tr></table></figure>

<h3 id="JSX-中的-Props"><a href="#JSX-中的-Props" class="headerlink" title="JSX 中的 Props"></a>JSX 中的 Props</h3><h4 id="字符串字面量"><a href="#字符串字面量" class="headerlink" title="字符串字面量"></a>字符串字面量</h4><p>​        你可以将字符串字面量赋值给 prop. 如下两个 JSX 表达式是等价的：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;MyComponent message=&quot;hello world&quot; /&gt;</span><br><span class="line"></span><br><span class="line">&lt;MyComponent message=&#123;&#x27;hello world&#x27;&#125; /&gt;</span><br></pre></td></tr></table></figure>

<p>​        当你将字符串字面量赋值给 prop 时，它的值是未转义的。所以，以下两个 JSX 表达式是等价的：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;MyComponent message=&quot;&amp;lt;3&quot; /&gt;</span><br><span class="line"></span><br><span class="line">&lt;MyComponent message=&#123;&#x27;&lt;3&#x27;&#125; /&gt;</span><br></pre></td></tr></table></figure>

<h4 id="Props-默认值为-“True”"><a href="#Props-默认值为-“True”" class="headerlink" title="Props 默认值为 “True”"></a>Props 默认值为 “True”</h4><p>​        如果你没给 prop 赋值，它的默认值是 <code>true</code>。以下两个 JSX 表达式是等价的：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;MyTextBox autocomplete /&gt;</span><br><span class="line"></span><br><span class="line">&lt;MyTextBox autocomplete=&#123;true&#125; /&gt;</span><br></pre></td></tr></table></figure>

<p>​        通常，我们不建议不传递 value 给 prop，因为这可能与 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Object_initializer#New_notations_in_ECMAScript_2015">ES6 对象简写</a>混淆，<code>&#123;foo&#125;</code> 是 <code>&#123;foo: foo&#125;</code> 的简写，而不是 <code>&#123;foo: true&#125;</code>。这样实现只是为了保持和 HTML 中标签属性的行为一致。</p>
<h4 id="属性展开"><a href="#属性展开" class="headerlink" title="属性展开"></a>属性展开</h4><p>​        如果你已经有了一个 props 对象，你可以使用展开运算符 <code>...</code> 来在 JSX 中传递整个 props 对象。以下两个组件是等价的：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">function App1() &#123;</span><br><span class="line">  return &lt;Greeting firstName=&quot;Ben&quot; lastName=&quot;Hector&quot; /&gt;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function App2() &#123;</span><br><span class="line">  const props = &#123;firstName: &#x27;Ben&#x27;, lastName: &#x27;Hector&#x27;&#125;;</span><br><span class="line">  return &lt;Greeting &#123;...props&#125; /&gt;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="JSX-子元素"><a href="#JSX-子元素" class="headerlink" title="JSX 子元素"></a>JSX 子元素</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">render() &#123;</span><br><span class="line">  // 不需要用额外的元素包裹列表元素！</span><br><span class="line">  return [</span><br><span class="line">    // 不要忘记设置 key :)</span><br><span class="line">    &lt;li key=&quot;A&quot;&gt;First item&lt;/li&gt;,</span><br><span class="line">    &lt;li key=&quot;B&quot;&gt;Second item&lt;/li&gt;,</span><br><span class="line">    &lt;li key=&quot;C&quot;&gt;Third item&lt;/li&gt;,</span><br><span class="line">  ];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="函数作为子元素"><a href="#函数作为子元素" class="headerlink" title="函数作为子元素"></a>函数作为子元素</h4><p>​        通常，JSX 中的 JavaScript 表达式将会被计算为字符串、React 元素或者是列表。不过，<code>props.children</code> 和其他 prop 一样，它可以传递任意类型的数据，而不仅仅是 React 已知的可渲染类型。例如，如果你有一个自定义组件，你可以把回调函数作为 <code>props.children</code> 进行传递：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">// 调用子元素回调 numTimes 次，来重复生成组件</span><br><span class="line">function Repeat(props) &#123;</span><br><span class="line">  let items = [];</span><br><span class="line">  for (let i = 0; i &lt; props.numTimes; i++) &#123;    items.push(props.children(i));</span><br><span class="line">  &#125;</span><br><span class="line">  return &lt;div&gt;&#123;items&#125;&lt;/div&gt;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function ListOfTenThings() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;Repeat numTimes=&#123;10&#125;&gt;</span><br><span class="line">      &#123;(index) =&gt; &lt;div key=&#123;index&#125;&gt;This is item &#123;index&#125; in the list&lt;/div&gt;&#125;    &lt;/Repeat&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="布尔类型、Null-以及-Undefined-将会忽略"><a href="#布尔类型、Null-以及-Undefined-将会忽略" class="headerlink" title="布尔类型、Null 以及 Undefined 将会忽略"></a>布尔类型、Null 以及 Undefined 将会忽略</h4><p>​        <code>false</code>, <code>null</code>, <code>undefined</code>, and <code>true</code> 是合法的子元素。但它们并不会被渲染。以下的 JSX 表达式渲染结果相同：</p>
<p>​        值得注意的是有一些 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy">“falsy” 值</a>，如数字 <code>0</code>，仍然会被 React 渲染。例如，以下代码并不会像你预期那样工作，因为当 <code>props.messages</code> 是空数组时，<code>0</code> 仍然会被渲染：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&lt;div&gt;</span><br><span class="line">  &#123;props.messages.length &amp;&amp;    &lt;MessageList messages=&#123;props.messages&#125; /&gt;</span><br><span class="line">  &#125;</span><br><span class="line">&lt;/div&gt;</span><br></pre></td></tr></table></figure>



<h2 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h2><p>​        UI 更新需要昂贵的 DOM 操作，因此 React 内部使用了几种巧妙的技术来最小化 DOM 操作次数。对于大部分应用而言，使用 React 时无需做大量优化工作就能拥有高性能的用户界面。尽管如此，你仍然有办法来加速你的 React 应用。</p>
<h3 id="虚拟化长列表"><a href="#虚拟化长列表" class="headerlink" title="虚拟化长列表"></a>虚拟化长列表</h3><p>​        如果你的应用渲染了长列表（上百甚至上千的数据），我们推荐使用“虚拟滚动”技术。这项技术会在有限的时间内仅渲染有限的内容，并奇迹般地降低重新渲染组件消耗的时间，以及创建 DOM 节点的数量。</p>
<p>​        <a target="_blank" rel="noopener" href="https://react-window.now.sh/">react-window</a> 和 <a target="_blank" rel="noopener" href="https://bvaughn.github.io/react-virtualized/">react-virtualized</a> 是热门的虚拟滚动库。 它们提供了多种可复用的组件，用于展示列表、网格和表格数据。 如果你想要一些针对你的应用做定制优化，你也可以创建你自己的虚拟滚动组件，就像 <a target="_blank" rel="noopener" href="https://medium.com/@paularmstrong/twitter-lite-and-high-performance-react-progressive-web-apps-at-scale-d28a00e780a3">Twitter 所做的</a>。</p>
<h3 id="避免调停"><a href="#避免调停" class="headerlink" title="避免调停"></a>避免调停</h3><p>​        React 构建并维护了一套内部的 UI 渲染描述。它包含了来自你的组件返回的 React 元素。该描述使得 React 避免创建 DOM 节点以及没有必要的节点访问，因为 DOM 操作相对于 JavaScript 对象操作更慢。虽然有时候它被称为“虚拟 DOM”，但是它在 React Native 中拥有相同的工作原理。</p>
<p>​        当一个组件的 props 或 state 变更，React 会将最新返回的元素与之前渲染的元素进行对比，以此决定是否有必要更新真实的 DOM。当它们不相同时，React 会更新该 DOM。</p>
<p>​        即使 React 只更新改变了的 DOM 节点，重新渲染仍然花费了一些时间。在大部分情况下它并不是问题，不过如果它已经慢到让人注意了，你可以通过覆盖生命周期方法 <code>shouldComponentUpdate</code> 来进行提速。该方法会在重新渲染前被触发。其默认实现总是返回 <code>true</code>，让 React 执行更新：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">shouldComponentUpdate(nextProps, nextState) &#123;</span><br><span class="line">  return true;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        如果你知道在什么情况下你的组件不需要更新，你可以在 <code>shouldComponentUpdate</code> 中返回 <code>false</code> 来跳过整个渲染过程。其包括该组件的 <code>render</code> 调用以及之后的操作。</p>
<p>​        在大部分情况下，你可以继承 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/react-api.html#reactpurecomponent"><code>React.PureComponent</code></a> 以代替手写 <code>shouldComponentUpdate()</code>。它用当前与之前 props 和 state 的浅比较覆写了 <code>shouldComponentUpdate()</code> 的实现。</p>
<h3 id="shouldComponentUpdate-的作用"><a href="#shouldComponentUpdate-的作用" class="headerlink" title="shouldComponentUpdate 的作用"></a>shouldComponentUpdate 的作用</h3><p>​        这是一个组件的子树。每个节点中，<code>SCU</code> 代表 <code>shouldComponentUpdate</code> 返回的值，而 <code>vDOMEq</code> 代表返回的 React 元素是否相同。最后，圆圈的颜色代表了该组件是否需要被调停。</p>
<p><a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/static/5ee1bdf4779af06072a17b7a0654f6db/cd039/should-component-update.png"><img src="/.io//should-component-update.png" alt="should component update"></a></p>
<p>​        节点 C2 的 <code>shouldComponentUpdate</code> 返回了 <code>false</code>，React 因而不会去渲染 C2，也因此 C4 和 C5 的 <code>shouldComponentUpdate</code> 不会被调用到。</p>
<p>​        对于 C1 和 C3，<code>shouldComponentUpdate</code> 返回了 <code>true</code>，所以 React 需要继续向下查询子节点。这里 C6 的 <code>shouldComponentUpdate</code> 返回了 <code>true</code>，同时由于渲染的元素与之前的不同使得 React 更新了该 DOM。</p>
<p>​        最后一个有趣的例子是 C8。React 需要渲染这个组件，但是由于其返回的 React 元素和之前渲染的相同，所以不需要更新 DOM。</p>
<p>​        显而易见，你看到 React 只改变了 C6 的 DOM。对于 C8，通过对比了渲染的 React 元素跳过了渲染。而对于 C2 的子节点和 C7，由于 <code>shouldComponentUpdate</code> 使得 <code>render</code> 并没有被调用。因此它们也不需要对比元素了。</p>
<h4 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">class CounterButton extends React.Component &#123;</span><br><span class="line">  constructor(props) &#123;</span><br><span class="line">    super(props);</span><br><span class="line">    this.state = &#123;count: 1&#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  shouldComponentUpdate(nextProps, nextState) &#123;</span><br><span class="line">    if (this.props.color !== nextProps.color) &#123;</span><br><span class="line">      return true;</span><br><span class="line">    &#125;</span><br><span class="line">    if (this.state.count !== nextState.count) &#123;</span><br><span class="line">      return true;</span><br><span class="line">    &#125;</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    return (</span><br><span class="line">      &lt;button</span><br><span class="line">        color=&#123;this.props.color&#125;</span><br><span class="line">        onClick=&#123;() =&gt; this.setState(state =&gt; (&#123;count: state.count + 1&#125;))&#125;&gt;</span><br><span class="line">        Count: &#123;this.state.count&#125;</span><br><span class="line">      &lt;/button&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="Portals"><a href="#Portals" class="headerlink" title="Portals"></a>Portals</h2><p>​        Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ReactDOM.createPortal(child, container)</span><br></pre></td></tr></table></figure>

<p>​        第一个参数（<code>child</code>）是任何<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/react-component.html#render">可渲染的 React 子元素</a>，例如一个元素，字符串或 fragment。第二个参数（<code>container</code>）是一个 DOM 元素。</p>
<h2 id="Profiler-API"><a href="#Profiler-API" class="headerlink" title="Profiler API"></a>Profiler API</h2><p>​        <code>Profiler</code> 测量一个 React 应用多久渲染一次以及渲染一次的“代价”。 它的目的是识别出应用中渲染较慢的部分，或是可以使用<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/hooks-faq.html#how-to-memoize-calculations">类似 memoization 优化</a>的部分，并从相关优化中获益。</p>
<blockquote>
<p>注意：</p>
<p>Profiling 增加了额外的开支，所以<strong>它在<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/optimizing-performance.html#use-the-production-build">生产构建</a>中会被禁用</strong>。</p>
<p>为了将 profiling 功能加入生产环境中，React 提供了使 profiling 可用的特殊的生产构建环境。 从 <a target="_blank" rel="noopener" href="https://fb.me/react-profiling">fb.me/react-profiling</a>了解更多关于如何使用这个构建环境的信息。</p>
</blockquote>
<h2 id="协调"><a href="#协调" class="headerlink" title="协调"></a>协调</h2><p>​        React 提供的声明式 API 让开发者可以在对 React 的底层实现并不了解的情况下编写应用。在开发者编写应用时，可以保持相对简单的心智，但开发者无法了解其内部的实现原理。本文描述了在实现 React 的 “diffing” 算法过程中所作出的设计决策，以保证组件更新可预测，且在繁杂业务场景下依然保持应用的高性能。</p>
<h3 id="设计动机"><a href="#设计动机" class="headerlink" title="设计动机"></a>设计动机</h3><p>​        在某一时间节点调用 React 的 <code>render()</code> 方法，会创建一棵由 React 元素组成的树。在下一次 state 或 props 更新时，相同的 <code>render()</code> 方法会返回一棵不同的树。React 需要基于这两棵树之间的差别来判断如何高效的更新 UI，以保证当前 UI 与最新的树保持同步。</p>
<p>​        通常一个最优算法里面。时间复杂度依然很高。</p>
<p>​        所以React在以下的两个基础之上提出了另外一套算法。同时也是diffing算法的核心。（我认为）</p>
<ul>
<li>两个不同类型的元素会产生出不同的树</li>
<li>开发者可以通过设置 key 属性，来告知那些子元素在不同的渲染下可以保存不变。</li>
</ul>
<h3 id="diffing-算法"><a href="#diffing-算法" class="headerlink" title="diffing 算法"></a>diffing 算法</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://zh-hans.reactjs.org/docs/reconciliation.html#the-diffing-algorithm</span><br></pre></td></tr></table></figure>

<h4 id="对比不同类型的元素"><a href="#对比不同类型的元素" class="headerlink" title="对比不同类型的元素"></a>对比不同类型的元素</h4><p>​        当根节点为不同类型的元素时，React 会拆卸原有的树并且建立起新的树。举个例子，当一个元素从 <code>&lt;a&gt;</code> 变成 <code>&lt;img&gt;</code>，从 <code>&lt;Article&gt;</code> 变成 <code>&lt;Comment&gt;</code>，或从 <code>&lt;Button&gt;</code> 变成 <code>&lt;div&gt;</code> 都会触发一个完整的重建流程。</p>
<h4 id="对比同一类型的元素"><a href="#对比同一类型的元素" class="headerlink" title="对比同一类型的元素"></a>对比同一类型的元素</h4><p>​        当对比两个相同类型的 React 元素时，React 会保留 DOM 节点，仅比对及更新有改变的属性。比如：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;div className=&quot;before&quot; title=&quot;stuff&quot; /&gt;</span><br><span class="line"></span><br><span class="line">&lt;div className=&quot;after&quot; title=&quot;stuff&quot; /&gt;</span><br></pre></td></tr></table></figure>

<p>​        通过对比这两个元素，React 知道只需要修改 DOM 元素上的 <code>className</code> 属性。</p>
<p>​        当更新 <code>style</code> 属性时，React 仅更新有所更变的属性。比如：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;div style=&#123;&#123;color: &#x27;red&#x27;, fontWeight: &#x27;bold&#x27;&#125;&#125; /&gt;</span><br><span class="line"></span><br><span class="line">&lt;div style=&#123;&#123;color: &#x27;green&#x27;, fontWeight: &#x27;bold&#x27;&#125;&#125; /&gt;</span><br></pre></td></tr></table></figure>

<p>​        通过对比这两个元素，React 知道只需要修改 DOM 元素上的 <code>color</code> 样式，无需修改 <code>fontWeight</code>。</p>
<p>​        在处理完当前节点之后，React 继续对子节点进行递归。</p>
<h4 id="对比同类型的组件元素"><a href="#对比同类型的组件元素" class="headerlink" title="对比同类型的组件元素"></a>对比同类型的组件元素</h4><p>​        当一个组件更新时，组件实例会保持不变，因此可以在不同的渲染时保持 state 一致。</p>
<p>​        下一步，调用 <code>render()</code> 方法，diff 算法将在之前的结果以及新的结果中进行递归。</p>
<h4 id="对子节点进行递归"><a href="#对子节点进行递归" class="headerlink" title="对子节点进行递归"></a>对子节点进行递归</h4><p>​        默认情况下，当递归 DOM 节点的子元素时，React 会同时遍历两个子元素的列表；当产生差异时，生成一个 mutation。</p>
<ul>
<li><p>在子元素列表末尾新增元素时，更新开销比较小。</p>
</li>
<li><p>如果只是简单的将新增元素插入到表头，那么更新开销会比较大。</p>
<ul>
<li>因为React 不会意识到应该保留其他元素。所以会导致开销比较大。</li>
<li>所以需要使用keys</li>
</ul>
</li>
</ul>
<h4 id="Keys"><a href="#Keys" class="headerlink" title="Keys"></a>Keys</h4><p>​        为了解决上述问题，React 引入了 <code>key</code> 属性。当子元素拥有 key 时，React 使用 key 来匹配原有树上的子元素以及最新树上的子元素。</p>
<p>​        当基于下标的组件进行重新排序时，组件 state 可能会遇到一些问题。由于组件实例是基于它们的 key 来决定是否更新以及复用，如果 key 是一个下标，那么修改顺序时会修改当前的 key，导致非受控组件的 state（比如输入框）可能相互篡改，会出现无法预期的变动。</p>
<p>​        在 Codepen 有两个例子，分别为 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/redirect-to-codepen/reconciliation/index-used-as-key">展示使用下标作为 key 时导致的问题</a>，以及<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/redirect-to-codepen/reconciliation/no-index-used-as-key">不使用下标作为 key 的例子的版本，修复了重新排列，排序，以及在列表头插入的问题</a>。</p>
<h4 id="权衡"><a href="#权衡" class="headerlink" title="权衡"></a>权衡</h4><p>​        我们定期优化启发式算法，让常见用例更高效地执行。在当前的实现中，可以理解为一棵子树能在其兄弟之间移动，但不能移动到其他位置。在这种情况下，算法会重新渲染整棵子树。</p>
<p>​        由于 React 依赖启发式算法，因此当以下假设没有得到满足，性能会有所损耗。</p>
<ol>
<li>该算法不会尝试匹配不同组件类型的子树。如果你发现你在两种不同类型的组件中切换，但输出非常相似的内容，建议把它们改成同一类型。在实践中，我们没有遇到这类问题。</li>
<li>Key 应该具有稳定，可预测，以及列表内唯一的特质。不稳定的 key（比如通过 <code>Math.random()</code> 生成的）会导致许多组件实例和 DOM 节点被不必要地重新创建，这可能导致性能下降和子组件中的状态丢失。</li>
</ol>
<h2 id="Refs-and-the-DOM"><a href="#Refs-and-the-DOM" class="headerlink" title="Refs and the DOM"></a>Refs and the DOM</h2><p>​        Refs 提供了一种方式，允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。</p>
<h3 id="回调Ref和普通ref的使用"><a href="#回调Ref和普通ref的使用" class="headerlink" title="回调Ref和普通ref的使用"></a>回调Ref和普通ref的使用</h3><h4 id="普通ref"><a href="#普通ref" class="headerlink" title="普通ref"></a>普通ref</h4><ul>
<li>需要使用 React.createRef(); 进行创建，否则不能ref成功</li>
<li>同时我们查看了 createRef 的方法里面实现了什么，发现就只是创建了一个 current 对象，然后再使用 Object.seal 将其对象进行了封闭操作。</li>
<li>同时如果我们创建一个对象。只要对象里面存在current属性，那么便可以存储ref</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">class Main extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    private inputRef:any</span><br><span class="line">    </span><br><span class="line">    constructor(props:any) &#123;</span><br><span class="line">        super(props);</span><br><span class="line">        </span><br><span class="line">        this.inputRef = React.createRef();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line">        return (</span><br><span class="line">            &lt;div&gt;</span><br><span class="line">                &lt;input type=&quot;text&quot; ref=&#123;this.inputRef&#125;/&gt;</span><br><span class="line">            &lt;/div&gt;</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="回调ref"><a href="#回调ref" class="headerlink" title="回调ref"></a>回调ref</h4><ul>
<li>不需要使用 React.createRef(); 进行初始化</li>
<li>通过使用回调函数，函数的第一个参数就是对应的DOM节点。</li>
<li>和普通ref一样使用ref属性attributes。</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">class Main extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    private inputRef:any</span><br><span class="line"></span><br><span class="line">    constructor(props:any) &#123;</span><br><span class="line">        super(props);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line">        const setInputRef = (el:any) =&gt; &#123;</span><br><span class="line">            this.inputRef = el</span><br><span class="line">        &#125;</span><br><span class="line">        return (</span><br><span class="line">            &lt;div&gt;</span><br><span class="line">                &lt;input type=&quot;text&quot; ref=&#123;setInputRef&#125;/&gt;</span><br><span class="line">            &lt;/div&gt;</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="关于回调-refs-的说明"><a href="#关于回调-refs-的说明" class="headerlink" title="关于回调 refs 的说明"></a>关于回调 refs 的说明</h4><p>​        如果 <code>ref</code> 回调函数是以内联函数的方式定义的，在更新过程中它会被执行两次，第一次传入参数 <code>null</code>，然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例，所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题，但是大多数情况下它是无关紧要的。</p>
<h2 id="Render-Props"><a href="#Render-Props" class="headerlink" title="Render Props"></a>Render Props</h2><p>​        术语 <a target="_blank" rel="noopener" href="https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce">“render prop”</a> 是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术</p>
<h3 id="使用-Render-Props-来解决横切关注点（Cross-Cutting-Concerns）"><a href="#使用-Render-Props-来解决横切关注点（Cross-Cutting-Concerns）" class="headerlink" title="使用 Render Props 来解决横切关注点（Cross-Cutting Concerns）"></a>使用 Render Props 来解决横切关注点（Cross-Cutting Concerns）</h3><p>​        简单来说就是vue的插槽，vue的插槽，就是说，可以在使用这个组件的时候，自己定义组件内部的显示为自己定义的组件。然后同时还需要使用作用域插槽来解决作用于的问题。貌似</p>
<p>​        首先，我们知道可以使用 this.props.children 的属性可以有一个插槽的效果</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">class Main extends React.Component&lt;any, any&gt; &#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">        return (</span><br><span class="line">            &lt;div&gt;</span><br><span class="line">                &#123;this.props.children&#125;</span><br><span class="line">            &lt;/div&gt;</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        但是，这样写，有一个作用域的问题，以及不能调用Main里面的属性。</p>
<p>​        所以此时就要使用 this.props.render 来代替 children 了</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">class Cat extends React.Component &#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    const mouse = this.props.mouse;</span><br><span class="line">    return (</span><br><span class="line">      &lt;img src=&quot;/cat.jpg&quot; style=&#123;&#123; position: &#x27;absolute&#x27;, left: mouse.x, top: mouse.y &#125;&#125; /&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class Mouse extends React.Component &#123;</span><br><span class="line">  constructor(props) &#123;</span><br><span class="line">    super(props);</span><br><span class="line">    this.handleMouseMove = this.handleMouseMove.bind(this);</span><br><span class="line">    this.state = &#123; x: 0, y: 0 &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  handleMouseMove(event) &#123;</span><br><span class="line">    this.setState(&#123;</span><br><span class="line">      x: event.clientX,</span><br><span class="line">      y: event.clientY</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    return (</span><br><span class="line">      &lt;div style=&#123;&#123; height: &#x27;100vh&#x27; &#125;&#125; onMouseMove=&#123;this.handleMouseMove&#125;&gt;</span><br><span class="line"></span><br><span class="line">        &#123;/*</span><br><span class="line">          使用 `render`prop 动态决定要渲染的内容，</span><br><span class="line">          而不是给出一个 &lt;Mouse&gt; 渲染结果的静态表示</span><br><span class="line">        */&#125;</span><br><span class="line">        &#123;this.props.render(this.state)&#125;</span><br><span class="line">      &lt;/div&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class MouseTracker extends React.Component &#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    return (</span><br><span class="line">      &lt;div&gt;</span><br><span class="line">        &lt;h1&gt;移动鼠标!&lt;/h1&gt;</span><br><span class="line">        &lt;Mouse render=&#123;mouse =&gt; (</span><br><span class="line">          &lt;Cat mouse=&#123;mouse&#125; /&gt;</span><br><span class="line">        )&#125;/&gt;</span><br><span class="line">      &lt;/div&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        其思想就是，首先，this.props.render 这个是一个props属性，所以render是调用组件时传递过来的。然后传递这个参数this.state 这个参数就会作为 mouse，然后传递给Cat组件的mouse属性。</p>
<p>​        然后这个的返回值又会渲染。标签。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">this.props.render(this.state)</span><br><span class="line"></span><br><span class="line">Mouse render=&#123;mouse =&gt; (</span><br><span class="line">	Cat mouse=&#123;mouse&#125;	</span><br><span class="line">)&#125;</span><br></pre></td></tr></table></figure>

<p>​        关于 render prop 一个有趣的事情是你可以使用带有 render prop 的常规组件来实现大多数<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/higher-order-components.html">高阶组件</a> (HOC)。 例如，如果你更喜欢使用 <code>withMouse</code> HOC而不是 <code>&lt;Mouse&gt;</code> 组件，你可以使用带有 render prop 的常规 <code>&lt;Mouse&gt;</code> 轻松创建一个：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">// 如果你出于某种原因真的想要 HOC，那么你可以轻松实现</span><br><span class="line">// 使用具有 render prop 的普通组件创建一个！</span><br><span class="line">function withMouse(Component) &#123;</span><br><span class="line">  return class extends React.Component &#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      return (</span><br><span class="line">        &lt;Mouse render=&#123;mouse =&gt; (</span><br><span class="line">          &lt;Component &#123;...this.props&#125; mouse=&#123;mouse&#125; /&gt;</span><br><span class="line">        )&#125;/&gt;</span><br><span class="line">      );</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        重要的是要记住，render prop 是因为模式才被称为 <em>render</em> prop ，你不一定要用名为 <code>render</code> 的 prop 来使用这种模式。事实上， <a target="_blank" rel="noopener" href="https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce"><em>任何</em>被用于告知组件需要渲染什么内容的函数 prop 在技术上都可以被称为 “render prop”</a>.</p>
<p>​        尽管之前的例子使用了 <code>render</code>，我们也可以简单地使用 <code>children</code> prop！</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;Mouse children=&#123;mouse =&gt; (</span><br><span class="line">  &lt;p&gt;鼠标的位置是 &#123;mouse.x&#125;，&#123;mouse.y&#125;&lt;/p&gt;</span><br><span class="line">)&#125;/&gt;</span><br></pre></td></tr></table></figure>

<p>​        记住，<code>children</code> prop 并不真正需要添加到 JSX 元素的 “attributes” 列表中。相反，你可以直接放置到元素的<em>内部</em>！</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;Mouse&gt;</span><br><span class="line">  &#123;mouse =&gt; (</span><br><span class="line">    &lt;p&gt;鼠标的位置是 &#123;mouse.x&#125;，&#123;mouse.y&#125;&lt;/p&gt;</span><br><span class="line">  )&#125;</span><br><span class="line">&lt;/Mouse&gt;</span><br></pre></td></tr></table></figure>

<p>​        这里，就是使用了 children 属性，我们可以知道，this.props.children 属性将作为组件的插槽形式进行传递。我们在调用组件的时候，直接作为内容，然后就会作为租价的children属性。</p>
<h3 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h3><h4 id="将-Render-Props-与-React-PureComponent-一起使用时要小心"><a href="#将-Render-Props-与-React-PureComponent-一起使用时要小心" class="headerlink" title="将 Render Props 与 React.PureComponent 一起使用时要小心"></a>将 Render Props 与 React.PureComponent 一起使用时要小心</h4><p>​        如果你在 render 方法里创建函数，那么使用 render prop 会抵消使用 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/react-api.html#reactpurecomponent"><code>React.PureComponent</code></a> 带来的优势。因为浅比较 props 的时候总会得到 false，并且在这种情况下每一个 <code>render</code> 对于 render prop 将会生成一个新的值。</p>
<h2 id="静态类型检查"><a href="#静态类型检查" class="headerlink" title="静态类型检查"></a>静态类型检查</h2><p>​        像 <a target="_blank" rel="noopener" href="https://flow.org/">Flow</a> 和 <a target="_blank" rel="noopener" href="https://www.typescriptlang.org/">TypeScript</a> 等这些静态类型检查器，可以在运行前识别某些类型的问题。他们还可以通过增加自动补全等功能来改善开发者的工作流程。出于这个原因，我们建议在大型代码库中使用 Flow 或 TypeScript 来代替 <code>PropTypes</code>。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npx create-react-app my-app --template typescript</span><br></pre></td></tr></table></figure>

<p>​        如需将 TypeScript 添加到<strong>现有的 Create React App 项目</strong>中，<a target="_blank" rel="noopener" href="https://facebook.github.io/create-react-app/docs/adding-typescript">请参考此文档</a>.</p>
<h3 id="添加-TypeScript-到现有项目中"><a href="#添加-TypeScript-到现有项目中" class="headerlink" title="添加 TypeScript 到现有项目中"></a>添加 TypeScript 到现有项目中</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://zh-hans.reactjs.org/docs/static-type-checking.html#adding-typescript-to-a-project</span><br></pre></td></tr></table></figure>

<ul>
<li>先下载typescript的安装包</li>
<li>然后在package.json 里面添加scripts指令。tsc就是将ts编译的指令。</li>
<li>创建tsconfig.json 的文件，用于typescript的编译配置<ul>
<li>可以使用 npm tsc –init</li>
<li><code>tsconfig.json</code> 文件中，有许多配置项用于配置编译器。查看所有配置项的的详细说明，<a target="_blank" rel="noopener" href="https://www.typescriptlang.org/docs/handbook/tsconfig-json.html">请参考此文档</a>。</li>
</ul>
</li>
</ul>
<h2 id="严格模式"><a href="#严格模式" class="headerlink" title="严格模式"></a>严格模式</h2><p>​        <code>StrictMode</code> 是一个用来突出显示应用程序中潜在问题的工具。与 <code>Fragment</code> 一样，<code>StrictMode</code> 不会渲染任何可见的 UI。它为其后代元素触发额外的检查和警告。</p>
<blockquote>
<p>注意：</p>
<p>严格模式检查仅在开发模式下运行；<em>它们不会影响生产构建</em>。</p>
</blockquote>
<p>​        你可以为应用程序的任何部分启用严格模式。例如：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">import React from &#x27;react&#x27;;</span><br><span class="line"></span><br><span class="line">function ExampleApplication() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;Header /&gt;</span><br><span class="line">      &lt;React.StrictMode&gt;        &lt;div&gt;</span><br><span class="line">          &lt;ComponentOne /&gt;</span><br><span class="line">          &lt;ComponentTwo /&gt;</span><br><span class="line">        &lt;/div&gt;</span><br><span class="line">      &lt;/React.StrictMode&gt;      &lt;Footer /&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        在上述的示例中，<em>不</em>会对 <code>Header</code> 和 <code>Footer</code> 组件运行严格模式检查。但是，<code>ComponentOne</code> 和 <code>ComponentTwo</code> 以及它们的所有后代元素都将进行检查。</p>
<p>​        <code>StrictMode</code> 目前有助于：</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/strict-mode.html#identifying-unsafe-lifecycles">识别不安全的生命周期</a></li>
<li><a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/strict-mode.html#warning-about-legacy-string-ref-api-usage">关于使用过时字符串 ref API 的警告</a></li>
<li><a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/strict-mode.html#warning-about-deprecated-finddomnode-usage">关于使用废弃的 findDOMNode 方法的警告</a></li>
<li><a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects">检测意外的副作用</a></li>
<li><a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/strict-mode.html#detecting-legacy-context-api">检测过时的 context API</a></li>
</ul>
<p>​        未来的 React 版本将添加更多额外功能。</p>
<h2 id="使用-PropTypes-进行类型检查"><a href="#使用-PropTypes-进行类型检查" class="headerlink" title="使用 PropTypes 进行类型检查"></a>使用 PropTypes 进行类型检查</h2><blockquote>
<p>注意：</p>
<p>自 React v15.5 起，<code>React.PropTypes</code> 已移入另一个包中。请使用 <a target="_blank" rel="noopener" href="https://www.npmjs.com/package/prop-types"><code>prop-types</code> 库</a> 代替。</p>
<p>我们提供了一个 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/blog/2017/04/07/react-v15.5.0.html#migrating-from-reactproptypes">codemod 脚本</a>来做自动转换。</p>
</blockquote>
<h2 id="非受控组件"><a href="#非受控组件" class="headerlink" title="非受控组件"></a>非受控组件</h2><p>​        在大多数情况下，我们推荐使用 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/forms.html#controlled-components">受控组件</a> 来处理表单数据。在一个受控组件中，表单数据是由 React 组件来管理的。另一种替代方案是使用非受控组件，这时表单数据将交由 DOM 节点来处理。</p>
<h3 id="默认值"><a href="#默认值" class="headerlink" title="默认值"></a>默认值</h3><p>​        在 React 渲染生命周期时，表单元素上的 <code>value</code> 将会覆盖 DOM 节点中的值。在非受控组件中，你经常希望 React 能赋予组件一个初始值，但是不去控制后续的更新。 在这种情况下, 你可以指定一个 <code>defaultValue</code> 属性，而不是 <code>value</code>。在一个组件已经挂载之后去更新 <code>defaultValue</code> 属性的值，不会造成 DOM 上值的任何更新。</p>
<h2 id="Web-Components"><a href="#Web-Components" class="headerlink" title="Web Components"></a>Web Components</h2><p>​        React 和 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/Web_Components">Web Components</a> 为了解决不同的问题而生。Web Components 为可复用组件提供了强大的封装，而 React 则提供了声明式的解决方案，使 DOM 与数据保持同步。两者旨在互补。作为开发人员，可以自由选择在 Web Components 中使用 React，或者在 React 中使用 Web Components，或者两者共存。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://tallgy.gitee.io/2022/02/15/JavaScript/JavaScript-%E7%B1%BB/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tallgy">
      <meta itemprop="description" content="只是一个知识的搬运工">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="tallgy's blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/02/15/JavaScript/JavaScript-%E7%B1%BB/" class="post-title-link" itemprop="url">JavaScript-类</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-02-15 11:18:08" itemprop="dateCreated datePublished" datetime="2022-02-15T11:18:08+08:00">2022-02-15</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2023-06-25 16:57:29" itemprop="dateModified" datetime="2023-06-25T16:57:29+08:00">2023-06-25</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JavaScript/" itemprop="url" rel="index"><span itemprop="name">JavaScript</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JavaScript/Class/" itemprop="url" rel="index"><span itemprop="name">Class</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="JavaScript-类"><a href="#JavaScript-类" class="headerlink" title="JavaScript 类"></a>JavaScript 类</h1><h2 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h2><h3 id="普通方法"><a href="#普通方法" class="headerlink" title="普通方法"></a>普通方法</h3><p>​        普通方法将会给继承给实例。同时方法是存在于实例的 <strong>__proto__</strong> 里面的，同时是处于不可枚举的状态。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">class c extends p &#123;</span><br><span class="line">	fn() &#123;</span><br><span class="line">	</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">let a = new c()</span><br><span class="line">a.fn()</span><br></pre></td></tr></table></figure>

<h3 id="静态方法"><a href="#静态方法" class="headerlink" title="静态方法"></a>静态方法</h3><p>​        静态方法是一个可以直接进行类名调用的方法。对象实例不能调用。静态方法可以继承。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">class c extends p &#123;</span><br><span class="line">	static fn() &#123;</span><br><span class="line">	</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">c.fn()</span><br></pre></td></tr></table></figure>

<h2 id="extends"><a href="#extends" class="headerlink" title="extends"></a>extends</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">class Children extends Parent &#123;</span><br><span class="line">	fn() &#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        简单来说就是继承，和Java差不多。创建一个Children类，然后继承了Parent，然后后面写的方法就是Children自身的方法。</p>
<h2 id="super"><a href="#super" class="headerlink" title="super"></a>super</h2><p>​        super 关键字，既可以当作函数使用，也可以当作对象使用。</p>
<h3 id="当作函数使用"><a href="#当作函数使用" class="headerlink" title="当作函数使用"></a>当作函数使用</h3><p>​        子类的构造函数必须要执行一次 super() 函数。用于调用父类的构造函数，并且只能在构造函数内调用。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">class children extends parent &#123;</span><br><span class="line">	constructor(name) &#123;</span><br><span class="line">		super(name)</span><br><span class="line">		...</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="当作对象使用"><a href="#当作对象使用" class="headerlink" title="当作对象使用"></a>当作对象使用</h3><p>​        super 作为对象时，在普通方法中，指向父类的原型对象；在静态方法中，指向父类。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://tallgy.gitee.io/2022/02/14/React/react%E5%AD%A6%E4%B9%A0-%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tallgy">
      <meta itemprop="description" content="只是一个知识的搬运工">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="tallgy's blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/02/14/React/react%E5%AD%A6%E4%B9%A0-%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/" class="post-title-link" itemprop="url">react学习-核心概念</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-02-14 19:30:54" itemprop="dateCreated datePublished" datetime="2022-02-14T19:30:54+08:00">2022-02-14</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2023-06-25 16:57:29" itemprop="dateModified" datetime="2023-06-25T16:57:29+08:00">2023-06-25</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/React/" itemprop="url" rel="index"><span itemprop="name">React</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="React-学习-核心概念-v17-0-2"><a href="#React-学习-核心概念-v17-0-2" class="headerlink" title="React 学习-核心概念 / v17.0.2"></a>React 学习-核心概念 / v17.0.2</h1><h2 id="jsx"><a href="#jsx" class="headerlink" title="jsx"></a>jsx</h2><h3 id="属性值"><a href="#属性值" class="headerlink" title="属性值"></a>属性值</h3><p>​        你可以通过使用引号，来将属性值指定为字符串字面量：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const element = &lt;a href=&quot;https://www.reactjs.org&quot;&gt; link &lt;/a&gt;;</span><br></pre></td></tr></table></figure>

<p>​        也可以使用大括号，来在属性值中插入一个 JavaScript 表达式：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const element = &lt;img src=&#123;user.avatarUrl&#125;&gt;&lt;/img&gt;;</span><br></pre></td></tr></table></figure>

<blockquote>
<p><strong>警告：</strong></p>
<p>因为 JSX 语法上更接近 JavaScript 而不是 HTML，所以 React DOM 使用 <code>camelCase</code>（小驼峰命名）来定义属性的名称，而不使用 HTML 属性名称的命名约定。</p>
<p>例如，JSX 里的 <code>class</code> 变成了 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/API/Element/className"><code>className</code></a>，而 <code>tabindex</code> 则变为 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/tabIndex"><code>tabIndex</code></a>。</p>
</blockquote>
<h3 id="jsx表示对象"><a href="#jsx表示对象" class="headerlink" title="jsx表示对象"></a>jsx表示对象</h3><p>babel会把jsx转移为一个 React.createElement的函数调用</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">const element = (</span><br><span class="line">  &lt;h1 className=&quot;greeting&quot;&gt;</span><br><span class="line">    Hello, world!</span><br><span class="line">  &lt;/h1&gt;</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">const element = React.createElement(</span><br><span class="line">  &#x27;h1&#x27;,</span><br><span class="line">  &#123;className: &#x27;greeting&#x27;&#125;,</span><br><span class="line">  &#x27;Hello, world!&#x27;</span><br><span class="line">);</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">这个是创建的对象的简化结构</span><br><span class="line">// 注意：这是简化过的结构</span><br><span class="line">const element = &#123;</span><br><span class="line">  type: &#x27;h1&#x27;,</span><br><span class="line">  props: &#123;</span><br><span class="line">    className: &#x27;greeting&#x27;,</span><br><span class="line">    children: &#x27;Hello, world!&#x27;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h2 id="元素渲染"><a href="#元素渲染" class="headerlink" title="元素渲染"></a>元素渲染</h2><h3 id="更新已渲染的元素"><a href="#更新已渲染的元素" class="headerlink" title="更新已渲染的元素"></a>更新已渲染的元素</h3><p>​        React 元素是<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Immutable_object">不可变对象</a>。一旦被创建，你就无法更改它的子元素或者属性。一个元素就像电影的单帧：它代表了某个特定时刻的 UI。</p>
<p>​        根据我们已有的知识，更新 UI 唯一的方式是创建一个全新的元素，并将其传入 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/react-dom.html#render"><code>ReactDOM.render()</code></a>。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">function tick() &#123;</span><br><span class="line">  const element = (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;h1&gt;Hello, world!&lt;/h1&gt;</span><br><span class="line">      &lt;h2&gt;It is &#123;new Date().toLocaleTimeString()&#125;.&lt;/h2&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">  ReactDOM.render(element, document.getElementById(&#x27;root&#x27;));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">setInterval(tick, 1000);</span><br></pre></td></tr></table></figure>

<p>​        通过每秒调用 render来重新渲染</p>
<p><strong>注意：</strong></p>
<ul>
<li><p>在实践中，大多数 React 应用只会调用一次 <a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/react-dom.html#render"><code>ReactDOM.render()</code></a>。在下一个章节，我们将学习如何将这些代码封装到<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/state-and-lifecycle.html">有状态组件</a>中。</p>
</li>
<li><p>我们建议你不要跳跃着阅读，因为每个话题都是紧密联系的。</p>
</li>
</ul>
<h3 id="React-只更新它需要更新的部分"><a href="#React-只更新它需要更新的部分" class="headerlink" title="React 只更新它需要更新的部分"></a>React 只更新它需要更新的部分</h3><p>​        React DOM 会将元素和它的子元素与它们之前的状态进行比较，并只会进行必要的更新来使 DOM 达到预期的状态。</p>
<p>​        就是说，我们虽然多次运行了 render，但是实际上只会更改必要的更新。</p>
<h2 id="组件-amp-amp-props"><a href="#组件-amp-amp-props" class="headerlink" title="组件 &amp;&amp; props"></a>组件 &amp;&amp; props</h2><h3 id="函数组件与-class-组件"><a href="#函数组件与-class-组件" class="headerlink" title="函数组件与 class 组件"></a>函数组件与 class 组件</h3><p>这两个组件是等效的。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">function Welcome(props) &#123;</span><br><span class="line">  return &lt;h1&gt;Hello, &#123;props.name&#125;&lt;/h1&gt;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class Welcome extends React.Component &#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    return &lt;h1&gt;Hello, &#123;this.props.name&#125;&lt;/h1&gt;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="渲染组件"><a href="#渲染组件" class="headerlink" title="渲染组件"></a>渲染组件</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">const element = &lt;div /&gt;;</span><br><span class="line"></span><br><span class="line">const element = &lt;Welcome name=&quot;Sara&quot; /&gt;;</span><br></pre></td></tr></table></figure>

<p>​        当 React 元素为用户自定义组件时，它会将 JSX 所接收的属性（attributes）以及子组件（children）转换为单个对象传递给组件，这个对象被称之为 “props”。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">function Welcome(props) &#123;</span><br><span class="line">  return &lt;h1&gt;Hello, &#123;props.name&#125;&lt;/h1&gt;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">这里传递的属性attributes 将会作为props对象里面的属性进行传递。class，className 都会使用props传递。</span><br><span class="line">const element = &lt;Welcome name=&quot;Sara&quot; /&gt;;</span><br><span class="line">ReactDOM.render(</span><br><span class="line">  element,</span><br><span class="line">  document.getElementById(&#x27;root&#x27;)</span><br><span class="line">);</span><br></pre></td></tr></table></figure>

<p><strong>注意：</strong> 组件名称必须以大写字母开头。</p>
<ul>
<li><p>React 会将以小写字母开头的组件视为原生 DOM 标签。例如，<code>&lt;div /&gt;</code> 代表 HTML 的 div 标签，而 <code>&lt;Welcome /&gt;</code> 则代表一个组件，并且需在作用域内使用 <code>Welcome</code>。</p>
</li>
<li><p>你可以在<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/jsx-in-depth.html#user-defined-components-must-be-capitalized">深入 JSX</a> 中了解更多关于此规范的原因。</p>
</li>
</ul>
<h3 id="组合组件"><a href="#组合组件" class="headerlink" title="组合组件"></a>组合组件</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">function Welcome(props) &#123;</span><br><span class="line">  return &lt;h1&gt;Hello, &#123;props.name&#125;&lt;/h1&gt;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function App() &#123;</span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;Welcome name=&quot;Sara&quot; /&gt;</span><br><span class="line">      &lt;Welcome name=&quot;Cahal&quot; /&gt;</span><br><span class="line">      &lt;Welcome name=&quot;Edite&quot; /&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">ReactDOM.render(</span><br><span class="line">  &lt;App /&gt;,</span><br><span class="line">  document.getElementById(&#x27;root&#x27;)</span><br><span class="line">);</span><br></pre></td></tr></table></figure>

<h3 id="Props的只读性"><a href="#Props的只读性" class="headerlink" title="Props的只读性"></a>Props的只读性</h3><p>​        <strong>所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。</strong></p>
<h2 id="State-amp-生命周期"><a href="#State-amp-生命周期" class="headerlink" title="State &amp; 生命周期"></a>State &amp; 生命周期</h2><h3 id="将函数组件转换为class组件"><a href="#将函数组件转换为class组件" class="headerlink" title="将函数组件转换为class组件"></a>将函数组件转换为class组件</h3><ul>
<li>创建同名class，继承于 React.Component</li>
<li>添加一个空的 render() 方法</li>
<li>将函数体移动到 render() 中</li>
<li>在render() 中，使用 this.props 替换 props</li>
<li>删除剩余的空函数声明。</li>
</ul>
<p>​        每次组件更新时 <code>render</code> 方法都会被调用，但只要在相同的 DOM 节点中渲染 <code>&lt;Clock /&gt;</code> ，就仅有一个 <code>Clock</code> 组件的 class 实例被创建使用。这就使得我们可以使用如 state 或生命周期方法等很多其他特性。</p>
<h3 id="向-class-组件中添加局部的-state"><a href="#向-class-组件中添加局部的-state" class="headerlink" title="向 class 组件中添加局部的 state"></a>向 class 组件中添加局部的 state</h3><ul>
<li><p>把 <code>render()</code> 方法中的 <code>this.props.date</code> 替换成 <code>this.state.date</code> ：</p>
</li>
<li><p>添加一个 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes#Constructor">class 构造函数</a>，然后在该函数中为 <code>this.state</code> 赋初值：</p>
</li>
<li><p>通过以下方式将 <code>props</code> 传递到父类的构造函数中：</p>
<ul>
<li>Class 组件应该始终使用 <code>props</code> 参数来调用父类的构造函数。</li>
</ul>
</li>
<li><p>移除 <code>&lt;Clock /&gt;</code> 元素中的 <code>date</code> 属性：</p>
</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">class Clock extends React.Component</span><br><span class="line">    &lt;any,</span><br><span class="line">        &#123;</span><br><span class="line">            date ?: any</span><br><span class="line">        &#125;&gt; &#123;</span><br><span class="line">    constructor(props: any) &#123;</span><br><span class="line">        super(props);</span><br><span class="line">        this.state = &#123;</span><br><span class="line">            date: new Date()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line">        return (</span><br><span class="line">            &lt;div&gt;</span><br><span class="line">                &lt;h1&gt;Hello&lt;/h1&gt;</span><br><span class="line">                &lt;h2&gt;it is &#123; this.state.date.toLocaleTimeString() &#125;&lt;/h2&gt;</span><br><span class="line">            &lt;/div&gt;</span><br><span class="line">        )</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="生命周期方法添加到-Class-中"><a href="#生命周期方法添加到-Class-中" class="headerlink" title="生命周期方法添加到 Class 中"></a>生命周期方法添加到 Class 中</h3><ul>
<li>当 <code>Clock</code> 组件第一次被渲染到 DOM 中的时候，就为其<a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval">设置一个计时器</a>。这在 React 中被称为“挂载（mount）”。</li>
<li>同时，当 DOM 中 <code>Clock</code> 组件被删除的时候，应该<a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval">清除计时器</a>。这在 React 中被称为“卸载（unmount）”。</li>
</ul>
<h4 id="生命周期方法"><a href="#生命周期方法" class="headerlink" title="生命周期方法"></a>生命周期方法</h4><ul>
<li> <code>componentDidMount()</code> 方法会在组件已经被渲染到 DOM 中后运行</li>
<li> <code>componentWillUnmount()</code> 生命周期方法中清除</li>
</ul>
<h5 id="创建-clock-组件"><a href="#创建-clock-组件" class="headerlink" title="创建 clock 组件"></a>创建 clock 组件</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">class Clock extends React.Component</span><br><span class="line">    &lt;</span><br><span class="line">        any,</span><br><span class="line">        &#123;</span><br><span class="line">            date : any</span><br><span class="line">        &#125;&gt; &#123;</span><br><span class="line">    private timerId: any;</span><br><span class="line">    constructor(props: any) &#123;</span><br><span class="line">        super(props);</span><br><span class="line">        this.state = &#123;</span><br><span class="line">            date: new Date()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    componentDidMount() &#123;</span><br><span class="line">        this.timerId = setInterval(</span><br><span class="line">            () =&gt; this.tick(),</span><br><span class="line">            1000</span><br><span class="line">        )</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    componentWillUnmount() &#123;</span><br><span class="line">        clearInterval(this.timerId)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    tick() &#123;</span><br><span class="line">        this.setState(&#123;</span><br><span class="line">            date: new Date()</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line">        return (</span><br><span class="line">            &lt;div&gt;</span><br><span class="line">                &lt;h1&gt;Hello&lt;/h1&gt;</span><br><span class="line">                &lt;h2&gt;it is &#123; this.state.date.toLocaleTimeString() &#125;&lt;/h2&gt;</span><br><span class="line">            &lt;/div&gt;</span><br><span class="line">        )</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="调用顺序"><a href="#调用顺序" class="headerlink" title="调用顺序"></a>调用顺序</h5><ul>
<li>当 <code>&lt;Clock /&gt;</code> 被传给 <code>ReactDOM.render()</code>的时候，React 会调用 <code>Clock</code> 组件的构造函数。</li>
<li>之后 React 会调用组件的 <code>render()</code> 方法。</li>
<li>当 <code>Clock</code> 的输出被插入到 DOM 中后，React 就会调用 <code>ComponentDidMount()</code> 生命周期方法。</li>
<li>浏览器每秒都会调用一次 <code>tick()</code> 方法。 </li>
<li>一旦 <code>Clock</code> 组件从 DOM 中被移除，React 就会调用 <code>componentWillUnmount()</code> 生命周期方法。</li>
</ul>
<h3 id="正确的修改-State"><a href="#正确的修改-State" class="headerlink" title="正确的修改 State"></a>正确的修改 State</h3><h4 id="不要直接修改state里面的属性"><a href="#不要直接修改state里面的属性" class="headerlink" title="不要直接修改state里面的属性"></a>不要直接修改state里面的属性</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">// wrong, 这样写不会重新渲染组件。</span><br><span class="line">this.state.comm = &#x27;he&#x27;</span><br><span class="line"></span><br><span class="line">// correct</span><br><span class="line">this.setState(&#123;comm: &#x27;he&#x27;&#125;)</span><br></pre></td></tr></table></figure>

<p>​        构造函数是唯一可以给 <code>this.state</code> 赋值的地方。</p>
<h4 id="state更新可能是异步的"><a href="#state更新可能是异步的" class="headerlink" title="state更新可能是异步的"></a>state更新可能是异步的</h4><p>​        出于性能考虑，React 可能会把多个 <code>setState()</code> 调用合并成一个调用。</p>
<p>​        因为 <code>this.props</code> 和 <code>this.state</code> 可能会异步更新，所以你不要依赖他们的值来更新下一个状态。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">// Wrong</span><br><span class="line">例如，此代码可能会无法更新计数器：</span><br><span class="line">this.setState(&#123;</span><br><span class="line">  counter: this.state.counter + this.props.increment,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>​        要解决这个问题，可以让 <code>setState()</code> 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数，将此次更新被应用时的 props 做为第二个参数：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// Correct</span><br><span class="line">this.setState((state, props) =&gt; (&#123;</span><br><span class="line">  counter: state.counter + props.increment</span><br><span class="line">&#125;));</span><br></pre></td></tr></table></figure>

<h4 id="State-的更新会被合并"><a href="#State-的更新会被合并" class="headerlink" title="State 的更新会被合并"></a>State 的更新会被合并</h4><p>​        当你调用 <code>setState()</code> 的时候，React 会把你提供的对象合并到当前的 state。</p>
<p>​        这里的合并是浅合并，所以 <code>this.setState(&#123;comments&#125;)</code> 完整保留了 <code>this.state.posts</code>， 但是完全替换了 <code>this.state.comments</code>。</p>
<h3 id="数据是向下流动的"><a href="#数据是向下流动的" class="headerlink" title="数据是向下流动的"></a>数据是向下流动的</h3><p>​        组件可以选择把它的 state 作为 props 向下传递到它的子组件中：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;FormattedDate date=&#123;this.state.date&#125; /&gt;</span><br></pre></td></tr></table></figure>

<p>​        <code>FormattedDate</code> 组件会在其 props 中接收参数 <code>date</code>，但是组件本身无法知道它是来自于 <code>Clock</code> 的 state，或是 <code>Clock</code> 的 props，还是手动输入的：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">function FormattedDate(props) &#123;</span><br><span class="line">  return &lt;h2&gt;It is &#123;props.date.toLocaleTimeString()&#125;.&lt;/h2&gt;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="事件处理"><a href="#事件处理" class="headerlink" title="事件处理"></a>事件处理</h2><ul>
<li>React 事件的命名采用小驼峰式（camelCase），而不是纯小写。</li>
<li>使用 JSX 语法时你需要传入一个函数作为事件处理函数，而不是一个字符串。</li>
</ul>
<p>​        例如，传统的 HTML：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;button onclick=&quot;activateLasers()&quot;&gt;</span><br><span class="line">  Activate Lasers</span><br><span class="line">&lt;/button&gt;</span><br></pre></td></tr></table></figure>

<p>​        在 React 中略微不同：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&lt;button onClick=&#123;activateLasers&#125;&gt;  Activate Lasers</span><br><span class="line">&lt;/button&gt;</span><br></pre></td></tr></table></figure>

<p>​        在 React 中另一个不同点是你不能通过返回 <code>false</code> 的方式阻止默认行为。你必须显式的使用 <code>preventDefault</code>。</p>
<p>​        在这里，<code>e</code> 是一个合成事件。React 根据 <a target="_blank" rel="noopener" href="https://www.w3.org/TR/DOM-Level-3-Events/">W3C 规范</a>来定义这些合成事件，所以你不需要担心跨浏览器的兼容性问题。React 事件与原生事件不完全相同。</p>
<p>​        但是，这样进行调用时，将无法使用 this，所以需要使用 bind 来进行绑定context上下文。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">正常的使用时。这里的this，因为是在onClick里面进行的调用，this将没有正确的指向。所以需要使用bind进行绑定。</span><br><span class="line">click(e ?: any) &#123;</span><br><span class="line">    console.log(this.props);</span><br><span class="line">    e.preventDefault()</span><br><span class="line">    console.log(&#x27;you are click&#x27;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">render() &#123;</span><br><span class="line">    return (</span><br><span class="line">        &lt;div&gt;</span><br><span class="line">            &lt;h1 onClick=&#123;this.click&#125;&gt;Hello&lt;/h1&gt;</span><br><span class="line">            &lt;h2&gt;it is &#123; this.state.date &#125;&lt;/h2&gt;</span><br><span class="line">        &lt;/div&gt;</span><br><span class="line">    )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">第一种，在constructor里面进行绑定this并返回</span><br><span class="line">constructor(props: any) &#123;</span><br><span class="line">    super(props);</span><br><span class="line">    this.state = &#123;</span><br><span class="line">        date: 1</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    this.click = this.click.bind(this)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">第二种，在onClick里面使用bind进行绑定上下文。</span><br><span class="line">render() &#123;</span><br><span class="line">    return (</span><br><span class="line">        &lt;div&gt;</span><br><span class="line">            &lt;h1 onClick=&#123;this.click.bind(this)&#125;&gt;Hello&lt;/h1&gt;</span><br><span class="line">            &lt;h2&gt;it is &#123; this.state.date &#125;&lt;/h2&gt;</span><br><span class="line">        &lt;/div&gt;</span><br><span class="line">    )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>如果不使用bind，可以使用两种方式。</p>
<p>​        如果你正在使用实验性的 <a target="_blank" rel="noopener" href="https://babeljs.io/docs/plugins/transform-class-properties/">public class fields 语法</a>，你可以使用 class fields 正确的绑定回调函数：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">// 此语法确保 `handleClick` 内的 `this` 已被绑定。</span><br><span class="line">// 注意: 这是 *实验性* 语法。</span><br><span class="line">// this。箭头函数来将this进行了定向指定。</span><br><span class="line">handleClick = () =&gt; &#123;</span><br><span class="line">  console.log(&#x27;this is:&#x27;, this);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        如果你没有使用 class fields 语法，你可以在回调中使用<a target="_blank" rel="noopener" href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions">箭头函数</a>：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">render() &#123;</span><br><span class="line">  // 此语法确保 `handleClick` 内的 `this` 已被绑定。</span><br><span class="line">  return (</span><br><span class="line">    &lt;button onClick=&#123;() =&gt; this.handleClick()&#125;&gt;</span><br><span class="line">      Click me</span><br><span class="line">    &lt;/button&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​        这个方式和上面是基本上是一样的。区别就是在于上面的语法是一个实验性语法。</p>
<h3 id="向事件处理程序传递参数"><a href="#向事件处理程序传递参数" class="headerlink" title="向事件处理程序传递参数"></a>向事件处理程序传递参数</h3><p>​        第一种，事件参数需要进行显式的传递</p>
<p>​        第二种，事件参数是使用的bind进行的绑定，所以会隐式的放在最后一个。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&lt;button onClick=&#123;(e) =&gt; this.deleteRow(id, e)&#125;&gt;Delete Row&lt;/button&gt;</span><br><span class="line">&lt;button onClick=&#123;this.deleteRow.bind(this, id)&#125;&gt;Delete Row&lt;/button&gt;</span><br></pre></td></tr></table></figure>



<h2 id="条件渲染"><a href="#条件渲染" class="headerlink" title="条件渲染"></a>条件渲染</h2><p>​        React 中的条件渲染和 JavaScript 中的一样，使用 JavaScript 运算符 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else"><code>if</code></a> 或者<a target="_blank" rel="noopener" href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Conditional_Operator">条件运算符</a>去创建元素来表现当前的状态，然后让 React 根据它们来更新 UI。</p>
<p>对于条件渲染我们可以使用几种方式：</p>
<ul>
<li><p>元素变量，使用if</p>
<ul>
<li>```<br>if () {<pre><code>&lt;LogoutButton onClick=&#123;this.handleLogoutClick&#125; /&gt;
</code></pre>
} else {<pre><code>&lt;LoginButton onClick=&#123;this.handleLoginClick&#125; /&gt;
</code></pre>
}<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">* 与运算符 &amp;&amp;， 注意，同时我们也可以知道，&#123;&#125; 里面如果是布尔类型，那么将不会生成dom，只有在属性里面attributes里面使用Boolean类型才会没有问题。</span><br><span class="line"></span><br><span class="line">  * ```</span><br><span class="line">    &#123; message &amp;&amp; &lt;h2&gt;</span><br><span class="line">              You have &#123;unreadMessages.length&#125; unread messages.</span><br><span class="line">            &lt;/h2&gt;&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>三目运算符</p>
<ul>
<li>```<br>The user is <b>{isLoggedIn ? ‘currently’ : ‘not’}</b> logged in.{isLoggedIn<pre><code>    ? &lt;LogoutButton onClick=&#123;this.handleLogoutClick&#125; /&gt;
    : &lt;LoginButton onClick=&#123;this.handleLoginClick&#125; /&gt;
  &#125;
</code></pre>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">    </span><br><span class="line"></span><br></pre></td></tr></table></figure>
function Greeting(props) {<br>const isLoggedIn = props.isLoggedIn;<br>if (isLoggedIn) {<br>return <UserGreeting>;<br>}<br>return <GuestGreeting>;<br>}</GuestGreeting></UserGreeting></li>
</ul>
</li>
</ul>
<p>ReactDOM.render(<br>  // Try changing to isLoggedIn={true}:<br>  <Greeting isloggedin="{false}">,<br>  document.getElementById(‘root’)<br>);</Greeting></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure>
<p>class Greeting extends React.Component&lt;any, any&gt; {<br>    constructor(props ?: any) {<br>        super(props);<br>    }</p>
<pre><code>render() &#123;
    return (
        this.props.loginState ?
            &lt;h1&gt;login&lt;/h1&gt; :
            &lt;h1&gt;logout&lt;/h1&gt;
    );
&#125;
</code></pre>
<p>}</p>
<p>class Clock extends React.Component&lt;any, {<br>    loginState ?: boolean<br>}&gt; {<br>    // private loginState: any;</p>
<pre><code>constructor(props ?: any) &#123;
    super(props);
    this.state = &#123;
        loginState: false,
    &#125;
&#125;

componentDidUpdate(prevProps: Readonly&lt;&#123;&#125;&gt;, prevState: Readonly&lt;&#123;&#125;&gt;, snapshot?: any) &#123;
    console.log(prevState)
&#125;

handleLoginClick() &#123;
    this.setState(&#123;
        loginState: true
    &#125;)
&#125;

handleLogoutClick() &#123;
    this.setState(&#123;
        loginState: false
    &#125;)
&#125;

render() &#123;
    let loginState = this.state.loginState
    let button = loginState ?
        &lt;button onClick=&#123;this.handleLogoutClick.bind(this)&#125;&gt;logout&lt;/button&gt; :
        &lt;button onClick=&#123;this.handleLoginClick.bind(this)&#125;&gt;login&lt;/button&gt;

    return (
        &lt;div&gt;
            &lt;Greeting loginState=&#123;loginState&#125;/&gt;
            &#123;button&#125;
        &lt;/div&gt;
    );
&#125;
</code></pre>
<p>}</p>
<p>ReactDOM.render(<br>    <Clock></Clock>,<br>    document.getElementById(‘root’)<br>)</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">### 阻止组件渲染</span><br><span class="line"></span><br><span class="line">​		可以通过render方法直接返回 null，来不进行渲染。</span><br><span class="line"></span><br><span class="line">​		在组件的 `render` 方法中返回 `null` 并不会影响组件的生命周期。例如，上面这个示例中，`componentDidUpdate` 依然会被调用。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">## 列表 &amp; key</span><br><span class="line"></span><br><span class="line">### 渲染多个组件</span><br><span class="line"></span><br><span class="line">​		下面，我们使用 Javascript 中的 [`map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) 方法来遍历 `numbers` 数组。将数组中的每个元素变成 `&lt;li&gt;` 标签，最后我们将得到的数组赋值给 `listItems`：</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>const numbers = [1, 2, 3, 4, 5];<br>const listItems = numbers.map((number) =&gt;<br>  <li>{number}</li><br>);</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### 基础列表组件</span><br><span class="line"></span><br><span class="line">​		当我们运行这段代码，将会看到一个警告 `a key should be provided for list items`，意思是当你创建一个元素时，必须包括一个特殊的 `key` 属性。我们将在下一节讨论这是为什么。</span><br><span class="line"></span><br><span class="line">​		key值用于diff算法的优化，Vue上面也有，所以不多赘述。</span><br><span class="line"></span><br><span class="line">​		我们这里只看一下 key 值是怎么进行使用的。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>function NumberList(props) {<br>  const numbers = props.numbers;<br>  const listItems = numbers.map((number) =&gt;<br>    <li key="{number.toString()}"><br>      {number}<br>    </li><br>  );<br>  return (<br>    <ul>{listItems}</ul><br>  );<br>}</p>
<p>const numbers = [1, 2, 3, 4, 5];<br>ReactDOM.render(<br>  <NumberList numbers="{numbers}">,<br>  document.getElementById(‘root’)<br>);</NumberList></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### key</span><br><span class="line"></span><br><span class="line">​		key 帮助 React 识别哪些元素改变了，比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。</span><br><span class="line"></span><br><span class="line">​		写法：</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>const numbers = [1, 2, 3, 4, 5];<br>const listItems = numbers.map((number) =&gt;<br>  <li key="{number.toString()}"><br>    {number}<br>  </li><br>);</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。</span><br><span class="line"></span><br><span class="line">​		当元素没有确定 id 的时候，万不得已你可以使用元素索引 index 作为 key。</span><br><span class="line"></span><br><span class="line">​		如果列表项目的顺序可能会变化，我们不建议使用索引来用作 key 值，因为这样做会导致性能变差，还可能引起组件状态的问题。可以看看 Robin Pokorny 的[深度解析使用索引作为 key 的负面影响](https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318)这一篇文章。如果你选择不指定显式的 key 值，那么 React 将默认使用索引用作为列表项目的 key 值。</span><br><span class="line"></span><br><span class="line">​		要是你有兴趣了解更多的话，这里有一篇文章[深入解析为什么 key 是必须的](https://zh-hans.reactjs.org/docs/reconciliation.html#recursing-on-children)可以参考。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### 用key提取组件</span><br><span class="line"></span><br><span class="line">​		元素的 key 只有放在就近的数组上下文中才有意义。</span><br><span class="line"></span><br><span class="line">​		比方说，如果你[提取](https://zh-hans.reactjs.org/docs/components-and-props.html#extracting-components)出一个 `ListItem` 组件，你应该把 key 保留在数组中的这个 `&lt;ListItem /&gt;` 元素上，而不是放在 `ListItem` 组件中的 `&lt;li&gt;` 元素上。</span><br><span class="line"></span><br><span class="line">​		简单来说，虽然listItem 里面就只有一个li，但是因为就近的数组上下文是使用的 listItem，所以我们就需要给listItem 来使用key，而不是把key写在li标签里面。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>function ListItem(props) {<br>  const value = props.value;<br>  return (<br>    // 错误！你不需要在这里指定 key：<br>    <li key="{value.toString()}"><br>      {value}<br>    </li><br>  );<br>}</p>
<p>function NumberList(props) {<br>  const numbers = props.numbers;<br>  const listItems = numbers.map((number) =&gt;<br>    // 错误！元素的 key 应该在这里指定：<br>    <ListItem value="{number}"><br>  );<br>  return (<br>    <ul><br>      {listItems}<br>    </ul><br>  );<br>}</ListItem></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure>
<p>function ListItem(props) {<br>  // 正确！这里不需要指定 key：<br>  return <li>{props.value}</li>;<br>}</p>
<p>function NumberList(props) {<br>  const numbers = props.numbers;<br>  const listItems = numbers.map((number) =&gt;<br>    // 正确！key 应该在数组的上下文中被指定<br>    <ListItem key="{number.toString()}" value="{number}"><br>  );<br>  return (<br>    <ul><br>      {listItems}<br>    </ul><br>  );<br>}</ListItem></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		一个好的经验法则是：在 `map()` 方法中的元素需要设置 key 属性。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### key值在兄弟节点之间必须唯一</span><br><span class="line"></span><br><span class="line">​		数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而，它们不需要是全局唯一的。当我们生成两个不同的数组时，我们可以使用相同的 key 值。</span><br><span class="line"></span><br><span class="line">​		key 会传递信息给 React ，但不会传递给你的组件。如果你的组件中需要使用 `key` 属性的值，请用其他属性名显式传递这个值：</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>const content = posts.map((post) =&gt;<br>  <Post key="{post.id}" id="{post.id}" title="{post.title}"><br>);</Post></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### 在jsx中嵌套map()</span><br><span class="line"></span><br><span class="line">​		JSX 允许在大括号中[嵌入任何表达式](https://zh-hans.reactjs.org/docs/introducing-jsx.html#embedding-expressions-in-jsx)，所以我们可以内联 `map()` 返回的结果：</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>function NumberList(props) {<br>  const numbers = props.numbers;<br>  return (<br>    <ul><br>      {numbers.map((number) =&gt;<br>        <ListItem key="{number.toString()}" value="{number}"><br>      )}<br>    </ListItem></ul><br>  );<br>}</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">## 表单</span><br><span class="line"></span><br><span class="line">### 受控组件</span><br><span class="line"></span><br><span class="line">​		在 HTML 中，表单元素（如`&lt;input&gt;`、 `&lt;textarea&gt;` 和 `&lt;select&gt;`）通常自己维护 state，并根据用户输入进行更新。而在 React 中，可变状态（mutable state）通常保存在组件的 state 属性中，并且只能通过使用 [`setState()`](https://zh-hans.reactjs.org/docs/react-component.html#setstate)来更新。</span><br><span class="line"></span><br><span class="line">​		我们可以把两者结合起来，使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<input type="text" value="{this.state.value}" onchange="{this.handleChange}">

<p>handleChange(event) {<br>    this.setState({value: event.target.value});<br>}</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		由于在表单元素上设置了 `value` 属性，因此显示的值将始终为 `this.state.value`，这使得 React 的 state 成为唯一数据源。由于 `handlechange` 在每次按键时都会执行并更新 React 的 state，因此显示的值将随着用户输入而更新。</span><br><span class="line"></span><br><span class="line">​		这里就是将value值给锁定了，然后使用了change方法来进行修改value，修改了state的value就会修改input的value，然后就会页面的修改。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<input type="text" value="{this.state.value}" onchange="{this.handleChange}">

<p>handleChange(event) {<br>  this.setState({value: event.target.value});<br>}</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		同时，我们也发现了，react的 onChange 事件，其实就是input事件。</span><br><span class="line"></span><br><span class="line">​		同时 text area 文本输入框，也是一样的使用 value 来进行值的操作。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### select 标签</span><br><span class="line"></span><br><span class="line">​		React 并不会使用 `selected` 属性，而是在根 `select` 标签上使用 `value` 属性。这在受控组件中更便捷，因为您只需要在根标签中更新它。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<select value="{this.state.value}" onchange="{this.handleChange}">
    <option value="grapefruit">葡萄柚</option>
    <option value="lime">酸橙</option>
    <option value="coconut">椰子</option>
    <option value="mango">芒果</option>
</select>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		通过value属性的变化，来确定selected的显示。</span><br><span class="line"></span><br><span class="line">&gt; 注意</span><br><span class="line">&gt;</span><br><span class="line">&gt; 你可以将数组传递到 `value` 属性中，以支持在 `select` 标签中选择多个选项：</span><br><span class="line">&gt;</span><br></pre></td></tr></table></figure>
> <select multiple="{true}" value="{['B'," 'c']}>
> <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### 文件 input 标签</span><br><span class="line"></span><br><span class="line">​		在 HTML 中，`&lt;input type=&quot;file&quot;&gt;` 允许用户从存储设备中选择一个或多个文件，将其上传到服务器，或通过使用 JavaScript 的 [File API](https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications) 进行控制。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
</select><input type="file">
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		因为它的 value 只读，所以它是 React 中的一个**非受控**组件。将与其他非受控组件[在后续文档中](https://zh-hans.reactjs.org/docs/uncontrolled-components.html#the-file-input-tag)一起讨论。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### 处理多个输入</span><br><span class="line"></span><br><span class="line">​		当需要处理多个 `input` 元素时，我们可以给每个元素添加 `name` 属性，并让处理函数根据 `event.target.name` 的值选择要执行的操作。</span><br><span class="line"></span><br><span class="line">​		我开始的方法是，通过传递一个参数，来代表是哪个input输入，然后再进行一个处理判断。</span><br><span class="line"></span><br><span class="line">​		这里记住，这个value是不能接收Boolean类型的。但是checked可以。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
通过传递参数来判断是哪个输入。然后再在handleInputChange事件里面进行判断处理

<p>&lt;input type=”number” id={‘input’} onChange={(e) =&gt; this.handleInputChange(e, ‘input’)} value={this.state.numberOfGuests}/&gt;</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		我们通过设置input的name，然后通过type来获取不同的值，并且通过name来进行赋值给state。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>handleInputChange(event) {<br>    const target = event.target;<br>    const value = target.type === ‘checkbox’ ? target.checked : target.value;<br>    const name = target.name;</p>
<pre><code>this.setState(&#123;
    [name]: value
&#125;);
</code></pre>
<p>}</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		这里使用了 ES6 [计算属性名称](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names)的语法更新给定输入名称对应的 state 值。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>interface IAddPlayerFormState {<br>    isGoing: boolean,<br>    numberOfGuests: number<br>}</p>
<p>handleInputChange(event ?: any) {<br>    const target = event.target<br>    const value = target.type===’checkbox’ ? target.checked : target.value<br>    const name = target.name</p>
<pre><code>this.setState(&#123;
    [name]: value
&#125; as Pick&lt;IAddPlayerFormState, keyof IAddPlayerFormState&gt;)
</code></pre>
<p>}</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### 受控输入空值</span><br><span class="line"></span><br><span class="line">​		在[受控组件](https://zh-hans.reactjs.org/docs/forms.html#controlled-components)上指定 `value` 的 prop 会阻止用户更改输入。如果你指定了 `value`，但输入仍可编辑，则可能是你意外地将 `value` 设置为 `undefined` 或 `null`。</span><br><span class="line"></span><br><span class="line">​		下面的代码演示了这一点。（输入最初被锁定，但在短时间延迟后变为可编辑。）</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>ReactDOM.render(<input value="hi">, mountNode);</p>
<p>setTimeout(function() {<br>  ReactDOM.render(<input value="{null}">, mountNode);<br>}, 1000);</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### 受控组件的替代品</span><br><span class="line"></span><br><span class="line">​		有时使用受控组件会很麻烦，因为你需要为数据变化的每种方式都编写事件处理函数，并通过一个 React 组件传递所有的输入 state。当你将之前的代码库转换为 React 或将 React 应用程序与非 React 库集成时，这可能会令人厌烦。在这些情况下，你可能希望使用[非受控组件](https://zh-hans.reactjs.org/docs/uncontrolled-components.html), 这是实现输入表单的另一种方式。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">## 状态提升</span><br><span class="line"></span><br><span class="line">​		状态提升，其实简单来说就是子组件可以修改数据传递给父组件。</span><br><span class="line"></span><br><span class="line">​		方法其实也是和vue的差不多。vue子组件可以通过 $emit 通知父组件进行方法的调用。而react是通过直接调用父组件传递过来的方法进行调用。this.props.onTemperatureChange(event.target.value) 这个方法就是父组件传递过来的方法。父组件的attributes 属性。onTemperatureChange=&#123;this.handleCelsiusChange&#125;。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>const scaleNames : ScaleState = {<br>    c: ‘Celsius’,<br>    f: ‘Fahrenheit’<br>}</p>
<p>interface ScaleState {<br>    [key : string]: string<br>}<br>interface valueState {<br>    scale ?: string,<br>    temperature : number,<br>}</p>
<p>class TemperatureInput extends React.Component&lt;any, valueState&gt; {<br>    constructor(props?:any) {<br>        super(props);</p>
<pre><code>    this.state = &#123;
        temperature: 0
    &#125;

    this.handleChange = this.handleChange.bind(this)
&#125;

handleChange(event : any) &#123;
    // this.setState(&#123;
    //     temperature: event.target.value
    // &#125;)
    this.props.onTemperatureChange(event.target.value)
&#125;

render() &#123;
    const temperature = this.props.temperature
    const scale = this.props.scale
    return (
        &lt;fieldset&gt;
            &lt;legend&gt;Enter temperature in &#123;scaleNames[scale]&#125;: &lt;/legend&gt;
            &lt;input
                type=&quot;text&quot;
                value=&#123;temperature&#125;
                onChange=&#123;this.handleChange&#125; /&gt;
        &lt;/fieldset&gt;
    );
&#125;
</code></pre>
<p>}</p>
<p>interface valueStateCalculator {<br>    temperature: string,<br>    scale: string<br>}<br>class Calculator extends React.Component&lt;any, valueStateCalculator&gt; {<br>    constructor(props ?: any) {<br>        super(props);</p>
<pre><code>    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this)
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this)

    this.state = &#123;temperature: &#39;&#39;, scale: &#39;c&#39;&#125;;
&#125;

handleCelsiusChange(temperature : string) &#123;
    this.setState(&#123;
        scale: &#39;c&#39;,
        temperature
    &#125;)
&#125;
handleFahrenheitChange(temperature : string) &#123;
    this.setState(&#123;
        scale: &#39;f&#39;,
        temperature
    &#125;);
&#125;

toCelsius(fahrenheit : number) &#123;
    return (fahrenheit - 32) * 5 / 9;
&#125;
toFahrenheit(celsius : number) &#123;
    return (celsius * 9 / 5) + 32;
&#125;
tryConvert(temperature:string, convert:Function) &#123;
    const input = parseFloat(temperature);
    if (Number.isNaN(input)) &#123;
        return &#39;&#39;;
    &#125;
    const output = convert(input);
    const rounded = Math.round(output * 1000) / 1000;
    return rounded.toString();
&#125;

render() &#123;
    const scale = this.state.scale
    const temperature = this.state.temperature
    const celsius = scale===&#39;f&#39; ?
        this.tryConvert(temperature, this.toCelsius) :
        temperature;
    const fahrenheit  = scale===&#39;c&#39; ?
        this.tryConvert(temperature, this.toFahrenheit) :
        temperature;

    return (
        &lt;div&gt;
            &lt;TemperatureInput
                scale=&#123;&#39;c&#39;&#125;
                temperature=&#123;celsius&#125;
                onTemperatureChange=&#123;this.handleCelsiusChange&#125;&gt;&lt;/TemperatureInput&gt;
            &lt;TemperatureInput
                scale=&#123;&#39;f&#39;&#125;
                temperature=&#123;fahrenheit&#125;
                onTemperatureChange=&#123;this.handleFahrenheitChange&#125;&gt;&lt;/TemperatureInput&gt;
        &lt;/div&gt;
    );
&#125;
</code></pre>
<p>}</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">## 组合 vs 继承</span><br><span class="line"></span><br><span class="line">### 包含关系</span><br><span class="line"></span><br><span class="line">​		有些组件无法提前知晓它们子组件的具体内容。在 `Sidebar`（侧边栏）和 `Dialog`（对话框）等展现通用容器（box）的组件中特别容易遇到这种情况。</span><br><span class="line"></span><br><span class="line">​		我们建议这些组件使用一个特殊的 `children` prop 来将他们的子组件传递到渲染结果中：</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>function FancyBorder(props) {<br>  return (<br>    &lt;div className={‘FancyBorder FancyBorder-‘ + props.color}&gt;<br>      {props.children}<br>    <br>  );<br>}</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure>
<p>function WelcomeDialog() {<br>  return (<br>    <FancyBorder color="blue"><br>      <h1 classname="Dialog-title"><br>        Welcome<br>      </h1><br>      <p classname="Dialog-message"><br>        Thank you for visiting our spacecraft!<br>      </p><br>    </FancyBorder><br>  );<br>}</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		这个就类似于vue组件的插槽式写法。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">​		命名props传递</span><br><span class="line"></span><br><span class="line">​		具名插槽，写法和vue不同</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>function SplitPane(props) {<br>  return (<br>    <div classname="SplitPane"><br>      <div classname="SplitPane-left"><br>        {props.left}<br>      </div><br>      <div classname="SplitPane-right"><br>        {props.right}<br>      </div><br>    </div><br>  );<br>}</p>
<p>function App() {<br>  return (<br>    &lt;SplitPane<br>      left={<br>        <Contacts><br>      }<br>      right={<br>        <Chat><br>      } /&gt;<br>  );<br>}</Chat></Contacts></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		定义时，通过直接调用 props.left 来进行调用，注意，这个插槽和props属性是共通的。所以其实对于react来说，并没有分插槽这个属性。reac内部会使用。自己的方法进行处理。不用我们进行操作。</span><br><span class="line"></span><br><span class="line">​		同时 react的 ‘具名插槽‘ 的使用形式是直接在组件里面进行调用，但是传值需要在属性里面进行传值，所以这就是说为什么react没有插槽这一概念的原因。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### 特例关系</span><br><span class="line"></span><br><span class="line">​		在 React 中，我们也可以通过组合来实现这一点。“特殊”组件可以通过 props 定制并渲染“一般”组件：</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>function Dialog(props) {<br>  return (<br>    <FancyBorder color="blue"><br>      <h1 classname="Dialog-title"><br>        {props.title}<br>      </h1><br>      <p classname="Dialog-message"><br>        {props.message}<br>      </p><br>    </FancyBorder><br>  );<br>}</p>
<p>function WelcomeDialog() {<br>  return (<br>    <Dialog title="Welcome" message="Thank you for visiting our spacecraft!"><br>  );<br>}</Dialog></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		简单来说就是显示是通过props的内容进行的显示，同时传递时通过传递这个对应的内容，让页面进行渲染。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">​		Props 和组合为你提供了清晰而安全地定制组件外观和行为的灵活方式。注意：组件可以接受任意 props，包括基本数据类型，React 元素以及函数。</span><br><span class="line"></span><br><span class="line">​		如果你想要在组件间复用非 UI 的功能，我们建议将其提取为一个单独的 JavaScript 模块，如函数、对象或者类。组件可以直接引入（import）而无需通过 extend 继承它们。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">## react哲学</span><br><span class="line"></span><br><span class="line">### 第一步：将设计好的 UI 划分为组件层级</span><br><span class="line"></span><br><span class="line">​		划分组件，分层。抽离组件。</span><br><span class="line"></span><br><span class="line">### 第二步：用 React 创建一个静态版本</span><br><span class="line"></span><br><span class="line">### 第三步：确定 UI state 的最小（且完整）表示</span><br><span class="line"></span><br><span class="line">通过问自己以下三个问题，你可以逐个检查相应数据是否属于 state：</span><br><span class="line"></span><br><span class="line">1. 该数据是否是**由父组件通过 props 传递而来**的？如果是，那它应该不是 state。</span><br><span class="line">2. 该数据**是否随时间的推移而保持不变**？如果是，那它应该也不是 state。</span><br><span class="line">3. 你**能否根据其他 state 或 props 计算出该数据的值**？如果是，那它也不是 state。</span><br><span class="line"></span><br><span class="line">### 第四步：确定 state 放置的位置</span><br><span class="line"></span><br><span class="line">对于应用中的每一个 state：</span><br><span class="line"></span><br><span class="line">- 找到根据这个 state 进行渲染的所有组件。</span><br><span class="line">- 找到他们的共同所有者（common owner）组件（在组件层级上高于所有需要该 state 的组件）。</span><br><span class="line">- 该共同所有者组件或者比它层级更高的组件应该拥有该 state。</span><br><span class="line">- 如果你找不到一个合适的位置来存放该 state，就可以直接创建一个新的组件来存放该 state，并将这一新组件置于高于共同所有者组件层级的位置</span><br><span class="line"></span><br><span class="line">### 第五步：添加反向数据流</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">​		先继承 react.component 组件的内容，然后再在在自己的方法里面添加一个render 方法。</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>class ShoppingList extends React.Component {<br>  render() {<br>    return (<br>      <div classname="shopping-list"><br>        <h1>Shopping List for {this.props.name}</h1><br>        <ul><br>          <li>Instagram</li><br>          <li>WhatsApp</li><br>          <li>Oculus</li><br>        </ul><br>      </div><br>    );<br>  }<br>}</p>
<p>// 用法示例: <ShoppingList name="Mark"></ShoppingList></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">​		上述代码等同于:</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>return React.createElement(‘div’, {className: ‘shopping-list’},<br>  React.createElement(‘h1’, /* … h1 children … <em>/),<br>  React.createElement(‘ul’, /</em> … ul children … */)<br>);</p>
<pre><code>
</code></pre>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://tallgy.gitee.io/2022/02/12/JavaScript/%E9%9A%8F%E7%AC%94/if%20in%20%E7%94%A8%E6%B3%95/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tallgy">
      <meta itemprop="description" content="只是一个知识的搬运工">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="tallgy's blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/02/12/JavaScript/%E9%9A%8F%E7%AC%94/if%20in%20%E7%94%A8%E6%B3%95/" class="post-title-link" itemprop="url">if in 用法</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-02-12 16:09:34" itemprop="dateCreated datePublished" datetime="2022-02-12T16:09:34+08:00">2022-02-12</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2023-06-25 16:57:29" itemprop="dateModified" datetime="2023-06-25T16:57:29+08:00">2023-06-25</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JavaScript/" itemprop="url" rel="index"><span itemprop="name">JavaScript</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JavaScript/%E9%9A%8F%E7%AC%94/" itemprop="url" rel="index"><span itemprop="name">随笔</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="if-in-的用法"><a href="#if-in-的用法" class="headerlink" title="if in 的用法"></a>if in 的用法</h1><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">const obj = &#123;</span><br><span class="line">  a: 1,</span><br><span class="line">  b: 2,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">obj.__proto__.c = 3</span><br><span class="line"></span><br><span class="line">Object.defineProperty(obj, &#x27;d&#x27;, &#123;</span><br><span class="line">  value: 123,</span><br><span class="line">  enumerable: false</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">if (&#x27;a&#x27; in obj) &#123;</span><br><span class="line">  console.log(&#x27;a&#x27;)</span><br><span class="line">&#125;</span><br><span class="line">if (&#x27;c&#x27; in obj) &#123;</span><br><span class="line">  console.log(&#x27;c&#x27;)</span><br><span class="line">&#125;</span><br><span class="line">if (&#x27;d&#x27; in obj) &#123;</span><br><span class="line">  console.log(&#x27;d&#x27;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">a</span><br><span class="line">c</span><br><span class="line">d</span><br></pre></td></tr></table></figure>



<p>所以我们可以知道，if ( in )</p>
<p>会把 自身的，原型链上的。以及不可枚举的都会显示。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://tallgy.gitee.io/2022/02/11/router/%E6%BA%90%E7%A0%81/router4/router4%E6%BA%90%E7%A0%81/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tallgy">
      <meta itemprop="description" content="只是一个知识的搬运工">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="tallgy's blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/02/11/router/%E6%BA%90%E7%A0%81/router4/router4%E6%BA%90%E7%A0%81/" class="post-title-link" itemprop="url">router4源码</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-02-11 19:49:25" itemprop="dateCreated datePublished" datetime="2022-02-11T19:49:25+08:00">2022-02-11</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2023-06-25 16:57:29" itemprop="dateModified" datetime="2023-06-25T16:57:29+08:00">2023-06-25</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/router/" itemprop="url" rel="index"><span itemprop="name">router</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="router4-源码的学习"><a href="#router4-源码的学习" class="headerlink" title="router4 源码的学习"></a>router4 源码的学习</h1><p>​        在学习router的时候，发现了一个地方，就是<strong>beforeRouteEnter</strong> 里面添加了next参数，然后就发现了，如果不调用就会出现问题，但是如果不添加next参数就不会出这个问题，然后就准备看看源码，刚开始看的源码时vue2的，所以现在重看一个vue3的。</p>
<p>​        <strong>rotuer.ts / 852</strong> </p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">// check in-component beforeRouteEnter</span><br><span class="line">guards = extractComponentsGuards(</span><br><span class="line">  enteringRecords,</span><br><span class="line">  &#x27;beforeRouteEnter&#x27;,</span><br><span class="line">  to,</span><br><span class="line">  from</span><br><span class="line">)</span><br><span class="line">guards.push(canceledNavigationCheck)</span><br></pre></td></tr></table></figure>

<ul>
<li><p>大概就是调用方法。这里的 <strong>enteringRecords</strong> 参数是通过 769 line 返回值</p>
<ul>
<li><p>```<br>const [leavingRecords, updatingRecords, enteringRecords] =</p>
<pre><code>  extractChangingRecords(to, from)
</code></pre>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">*  **extractChangingRecords** 方法。1238 line。</span><br><span class="line"></span><br><span class="line">  * 记住是返回数组，然后数组是代表了会触发 leaving，enter，update 的记录。代码后面有分析，这里就不占空间了。</span><br><span class="line"></span><br><span class="line">*  **navigate** 方法， 763 line</span><br><span class="line"></span><br><span class="line">  * ```</span><br><span class="line">    大概就是，先获取到路由的记录</span><br><span class="line">    </span><br><span class="line">    const [leavingRecords, updatingRecords, enteringRecords] =</span><br><span class="line">          extractChangingRecords(to, from)</span><br></pre></td></tr></table></figure>

<ul>
<li>```<br>调用 beforeRouteLeave，注意，这里的leavingRecoreds 记录使用了 reverse 进行了逆序。所以触发会从下到上。同时 reverse会修改原数组。guards = extractComponentsGuards(<br>  leavingRecords.reverse(),<br>  ‘beforeRouteLeave’,<br>  to,<br>  from<br>)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">* ```</span><br><span class="line">  循环 leaving 记录。</span><br><span class="line">  </span><br><span class="line">  </span><br><span class="line">  // leavingRecords is already reversed</span><br><span class="line">  for (const record of leavingRecords) &#123;</span><br><span class="line">    record.leaveGuards.forEach(guard =&gt; &#123;</span><br><span class="line">      guards.push(guardToPromiseFn(guard, to, from))</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="router-ts-createRouter方法-355"><a href="#router-ts-createRouter方法-355" class="headerlink" title="router/ts    createRouter方法    355"></a>router/ts    createRouter方法    355</h2><h3 id="navigate-方法-763"><a href="#navigate-方法-763" class="headerlink" title="navigate 方法 763"></a>navigate 方法 763</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">// 返回要触发对应声明周期的 路由的记录</span><br><span class="line">const [leavingRecords, updatingRecords, enteringRecords] =</span><br><span class="line">	extractChangingRecords(to, from)</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">// all components here have been resolved once because we are leaving</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">guards = extractComponentsGuards(</span><br><span class="line">  leavingRecords.reverse(),</span><br><span class="line">  &#x27;beforeRouteLeave&#x27;,</span><br><span class="line">  to,</span><br><span class="line">  from</span><br><span class="line">)</span><br></pre></td></tr></table></figure>



<h2 id="extractChangingRecords-方法。"><a href="#extractChangingRecords-方法。" class="headerlink" title="extractChangingRecords 方法。"></a><strong>extractChangingRecords</strong> 方法。</h2><ul>
<li>```<br>const enteringRecords: RouteRecordNormalized[] = [] 代表定义的是一个数组。类型是 RouteRecordNormalized <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">* ```</span><br><span class="line">  function extractChangingRecords(</span><br><span class="line">    to: RouteLocationNormalized,</span><br><span class="line">    from: RouteLocationNormalizedLoaded</span><br><span class="line">  ) &#123;</span><br><span class="line">    // 数组类型</span><br><span class="line">    const leavingRecords: RouteRecordNormalized[] = []</span><br><span class="line">    const updatingRecords: RouteRecordNormalized[] = []</span><br><span class="line">    const enteringRecords: RouteRecordNormalized[] = []</span><br><span class="line">  </span><br><span class="line">    // matched 代表的是匹配上的路由部分。</span><br><span class="line">    //这里就是寻找len要比较长的一个。</span><br><span class="line">    const len = Math.max(from.matched.length, to.matched.length)</span><br><span class="line">    for (let i = 0; i &lt; len; i++) &#123;</span><br><span class="line">      // 这里就是 recordFrom 就让他等于 from 的匹配路由</span><br><span class="line">      const recordFrom = from.matched[i]</span><br><span class="line">      //如果路由存在，那么就会</span><br><span class="line">      // 从 to 里面匹配上的路由 进行find方法。轮询查看是否有满足的。</span><br><span class="line">      // isSameRouteRecord 方法就是 判断两个路由是不是一个。这里使用了 aliasOf 定义此记录是否是另一个记录的别名。如果该记录是原始记录，则此属性为 undefined。</span><br><span class="line">      if (recordFrom) &#123;</span><br><span class="line">        // 如果轮询找到了，那么就会把这个 from 的匹配路由push为 update的记录。</span><br><span class="line">        // 如果找不到，说明没有，那么就会 push 一个 leaving记录。</span><br><span class="line">        if (to.matched.find(record =&gt; isSameRouteRecord(record, recordFrom)))</span><br><span class="line">          updatingRecords.push(recordFrom)</span><br><span class="line">        else leavingRecords.push(recordFrom)</span><br><span class="line">      &#125;</span><br><span class="line">      // 记录此时的 to 的匹配路由</span><br><span class="line">      const recordTo = to.matched[i]</span><br><span class="line">      // 如果存在</span><br><span class="line">      if (recordTo) &#123;</span><br><span class="line">        // the type doesn&#x27;t matter because we are comparing per reference</span><br><span class="line">        // 判断原路由是否存在，不存在则会push为 enter 记录</span><br><span class="line">        if (!from.matched.find(record =&gt; isSameRouteRecord(record, recordTo))) &#123;</span><br><span class="line">          enteringRecords.push(recordTo)</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">    // 最后返回所有的记录</span><br><span class="line">    return [leavingRecords, updatingRecords, enteringRecords]</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></li>
</ul>
<h2 id="navigationGuards-ts"><a href="#navigationGuards-ts" class="headerlink" title="navigationGuards/ts"></a>navigationGuards/ts</h2><h3 id="extractComponentsGuards方法-230"><a href="#extractComponentsGuards方法-230" class="headerlink" title="extractComponentsGuards方法 230"></a>extractComponentsGuards方法 230</h3><h3 id="isRouteComponent-方法-357"><a href="#isRouteComponent-方法-357" class="headerlink" title="isRouteComponent 方法 357"></a>isRouteComponent 方法 357</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * Allows differentiating lazy components from functional components and vue-class-component</span><br><span class="line"> * 允许区分惰性组件与功能组件和vue类组件</span><br><span class="line"> *</span><br><span class="line"> * @param component</span><br><span class="line"> */</span><br><span class="line">function isRouteComponent(</span><br><span class="line">  component: RawRouteComponent</span><br><span class="line">): component is RouteComponent &#123;</span><br><span class="line">  // 组件要为对象，且存在 displayName，props，__vccOpts属性</span><br><span class="line">  return (</span><br><span class="line">    typeof component === &#x27;object&#x27; ||</span><br><span class="line">    &#x27;displayName&#x27; in component ||</span><br><span class="line">    &#x27;props&#x27; in component ||</span><br><span class="line">    &#x27;__vccOpts&#x27; in component</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="guardToPromiseFn方法-110"><a href="#guardToPromiseFn方法-110" class="headerlink" title="guardToPromiseFn方法 110"></a>guardToPromiseFn方法 110</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure>


      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://tallgy.gitee.io/2022/02/11/router/%E6%BA%90%E7%A0%81/router3%E6%BA%90%E7%A0%81/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tallgy">
      <meta itemprop="description" content="只是一个知识的搬运工">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="tallgy's blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/02/11/router/%E6%BA%90%E7%A0%81/router3%E6%BA%90%E7%A0%81/" class="post-title-link" itemprop="url">router3源码初学</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-02-11 16:00:22" itemprop="dateCreated datePublished" datetime="2022-02-11T16:00:22+08:00">2022-02-11</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2023-06-25 16:57:29" itemprop="dateModified" datetime="2023-06-25T16:57:29+08:00">2023-06-25</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/router/" itemprop="url" rel="index"><span itemprop="name">router</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="router3-源码初学"><a href="#router3-源码初学" class="headerlink" title="router3 源码初学"></a>router3 源码初学</h1><p>​        最近在学vue-router，然后在学习的过程中，顺便简单的翻阅翻阅源码来看看一些问题。后续也会认真的查看源码部分。</p>
<p>​        这个是 vue2 的部分。</p>
<h2 id="beforeRouteEnter"><a href="#beforeRouteEnter" class="headerlink" title="beforeRouteEnter"></a>beforeRouteEnter</h2><p>​        在 beforeRouteEnter 中，我们发现如果在创建方法的时候创建了回调函数</p>
<p>​        我们发现如果定义了形参，那么就需要调用，否则就会无法跳转。这就很奇怪。</p>
<p>​        所以我现在先看看代码，因为是第一次看，所以也有很多看不懂，所以会猜测。</p>
<p>先，在src目录里面搜寻 <strong>beforeRouteEnter</strong> ，然后查看 src/history/base 里面存在这个。<strong>extractEnterGuards</strong></p>
<p><img src="/.io//router3%E6%BA%90%E7%A0%81%5Cimage-20220211173002676.png" alt="image-20220211173002676"></p>
<p>​        可以发现，是在这里进行了使用。这个函数就是通过传递一个<strong>activated</strong>数组。然后调用 <strong>extractGuards</strong> 。这里activated就是路由匹配后，会处于routeEnter状态的路由数组。</p>
<p>​        <img src="/.io//image-20220211173238506.png" alt="image-20220211173238506"></p>
<p>​        这个函数就是创建 guards，获取<strong>flatMapComponents</strong> 返回的数据。然后再调用<strong>flatten</strong> 作为返回值。</p>
<p>​        <strong>flatMapComponents</strong> 方法就是将每个matched里面的数组元素进行传递给 <strong>flatten</strong> </p>
<p>其中，是从m.componets 里面获取key，然后循环，拿出 自己的 组件，实例，和m，key。然后传递给fn 进行调用，然后将返回值作为一个数组，然后循环了matched的length那么多次，最后返回给了 <strong>flatten</strong> 进行了合并。</p>
<p><img src="/.io//image-20220211175019270.png" alt="image-20220211175019270"> </p>
<p>​        <strong>flatten</strong> 合并数组，扁平化处理。</p>
<p>这个是 route.matched 的路由数组</p>
<img src="/.io//image-20220211184544135.png" alt="image-20220211184544135" style="zoom:67%;">

<p>这里传递的参数是，先 matched.map(m =&gt; {})，先取出匹配上的路由。进行循环。然后将路由里面的 m.components 就是获取路由里面的components，默认就是一个 default，然后就是map循环路由里面的components。然后取出components。instances，路由本身。key（就是组件名字。）</p>
<img src="/.io//image-20220211190158819.png" alt="image-20220211190158819" style="zoom:67%;">



<p><img src="/.io//image-20220211173522407.png" alt="image-20220211173522407"></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[1, 2], [2, 3] =&gt; [1, 2, 2, 3]</span><br></pre></td></tr></table></figure>

<p><strong>flatMapComponents</strong> 的fn是传递的方法</p>
<p><img src="/.io//image-20220211174408513.png" alt="image-20220211174408513"></p>
<p>​        <strong>guard</strong>：就是调用这个 extractGuard 的返回值，参数分别为 def：m.components[key] 和 name beforeRouteEnter。</p>
<p><img src="/.io//image-20220211175554195.png" alt="image-20220211175554195"></p>
<p>​        这个方法，先判断 def 是不是一个函数。然后对于不是函数的，调用 Vue.extend 方法。这个方法通过查看 API 可以知道是。</p>
<img src="/.io//image-20220211175816841.png" alt="image-20220211175816841" style="zoom:67%;">

<p>​        简单来看，就像是，先将def进行了extend，然后拿出里面的 options 的 beforeRouteEnter 参数。就是说从组件里面取出beforeRouteEnter方法。</p>
<img src="/.io//image-20220211180402862.png" alt="image-20220211180402862" style="zoom:67%;">

<p>​        所以，知道了 guard 是返回一个 自己定义的 <strong>beforeRouteEnter</strong> 方法。同时 <strong>Vue.extend(def).options[‘beforeRouteEnter’]</strong> 返回的是一个数组（这里我认为应该是Vue的处理，方便混入或者其他情况的解决。但是对于源码我还没有了解。所以不怎么清楚）。</p>
<p>​        然后继续进入 <strong>flatMapComponents</strong> 查看 322 行。进行bind方法的调用（bind 函数是<strong>extractGuards</strong> 传递过来的一个方法）。</p>
<p><img src="/.io//image-20220211185433641.png" alt="image-20220211185433641"></p>
<p>​        通过查看 extractEnterGuards 可以发现，这个会调用bind，然后return 一个 bindEnterGuard。</p>
<p><img src="/.io//image-20220211190859440.png" alt="image-20220211190859440"></p>
<p>​        我们可以查看 bindEnterGuard 方法。来查看里面的跳转情况。</p>
<p>​        这个方法就是返回一个 routeEnterGuard 方法。然后在方法里面会返回一个guard的调用的返回值。这里guard 就是对应的 <strong>beforeRouteEnter</strong> 方法。</p>
<p>​        通过这个方法我们可以知道，<strong>routeEnterGuard</strong> 里面 guard 是我们自定的 <strong>beforeRouteEnter</strong> 方法。里面的参数中，第三个参数的箭头函数，就是代表了我们的 <strong>beforeRouteEnter</strong> 方法中的 next。同时我们如果输出next也可以发现是正确的。</p>
<p>​        所以我们在 beforeRouteEnter 里面调用next() ，其实就是调用这个方法。然后如果我们传递了参数，那么我们就会进行里面的操作。</p>
<img src="/.io//image-20220211191050870.png" alt="image-20220211191050870" style="zoom:67%;">

<p>​        同时我们发现，如果我们不调用next方法。这个 guard方法是不会调用的。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/page/2/"><i class="fa fa-angle-left" aria-label="上一页"></i></a><a class="page-number" href="/">1</a><a class="page-number" href="/page/2/">2</a><span class="page-number current">3</span><a class="page-number" href="/page/4/">4</a><span class="space">&hellip;</span><a class="page-number" href="/page/11/">11</a><a class="extend next" rel="next" href="/page/4/"><i class="fa fa-angle-right" aria-label="下一页"></i></a>
  </nav>

</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">tallgy</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <script src="https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  





  





</body>
</html>
